这是来自第三方库API的真实示例,但已简化.
使用Oracle JDK 8u72编译
考虑这两种方法:
<X extends CharSequence> X getCharSequence() {
return (X) "hello";
}
<X extends String> X getString() {
return (X) "hello";
}
Run Code Online (Sandbox Code Playgroud)
两者都报告了"未经检查的演员"警告 - 我明白了.困扰我的是我为什么打电话
Integer x = getCharSequence();
Run Code Online (Sandbox Code Playgroud)
它编译?编译器应该知道Integer没有实现CharSequence.打电话给
Integer y = getString();
Run Code Online (Sandbox Code Playgroud)
给出错误(如预期的那样)
Run Code Online (Sandbox Code Playgroud)incompatible types: inference variable X has incompatible upper bounds java.lang.Integer,java.lang.String
有人可以解释为什么这种行为被认为是有效的?它会有用吗?
客户端不知道此调用是不安全的 - 客户端的代码在没有警告的情况下编译.为什么编译器不会警告/发出错误?
另外,它与这个例子有什么不同:
<X extends CharSequence> void doCharSequence(List<X> l) {
}
List<CharSequence> chsL = new ArrayList<>();
doCharSequence(chsL); // compiles
List<Integer> intL = new ArrayList<>();
doCharSequence(intL); // …Run Code Online (Sandbox Code Playgroud) 我偶然发现了一段代码,让我想知道为什么它成功编译:
public class Main {
public static void main(String[] args) {
String s = newList(); // why does this line compile?
System.out.println(s);
}
private static <T extends List<Integer>> T newList() {
return (T) new ArrayList<Integer>();
}
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,如果我修改方法的签名newList与<T extends ArrayList<Integer>>它不工作了.
注释和响应后更新: 如果我将泛型类型从方法移动到类,则代码不再编译:
public class SomeClass<T extends List<Integer>> {
public void main(String[] args) {
String s = newList(); // this doesn't compile anymore
System.out.println(s);
}
private T newList() {
return (T) new ArrayList<Integer>();
}
}
Run Code Online (Sandbox Code Playgroud) 为什么以下代码编译?该方法IElement.getX(String)返回该类型IElement或其子类的实例.类中的代码Main调用该getX(String)方法.编译器允许将返回值存储到类型的变量中Integer(显然不在层次结构中IElement).
public interface IElement extends CharSequence {
<T extends IElement> T getX(String value);
}
public class Main {
public void example(IElement element) {
Integer x = element.getX("x");
}
}
Run Code Online (Sandbox Code Playgroud)
返回类型是否仍然是一个实例IElement - 即使在类型擦除之后?
该getX(String)方法的字节码是:
public abstract <T extends IElement> T getX(java.lang.String);
flags: ACC_PUBLIC, ACC_ABSTRACT
Signature: #7 // <T::LIElement;>(Ljava/lang/String;)TT;
Run Code Online (Sandbox Code Playgroud)
编辑:替换String与一致Integer.
假设我们有2节课.一个空类Base,以及该类的子类Derived.
public class Base {}
public class Derived extends Base {}
Run Code Online (Sandbox Code Playgroud)
然后我们在另一个类中有几个方法:
import java.util.Collection
public class Consumer {
public void test() {
set(new Derived(), new Consumer().get());
}
public <T extends Base> T get() {
return (T) new Derived();
}
public void set(Base i, Derived b) {
System.out.println("base");
}
public void set(Derived d, Collection<? extends Consumer> o) {
System.out.println("object");
}
}
Run Code Online (Sandbox Code Playgroud)
这在Java 7中成功编译并运行,但不能在Java 8中编译.错误:
Error:(8, 9) java: reference to set is ambiguous
both method set(Base,Derived) in Consumer …Run Code Online (Sandbox Code Playgroud) 据我所知,Java中泛型的主要目的之一是提供编译时类型安全性.如果它被编译,代码将运行没有问题.
那么为什么要编译以下代码?
public static void main(String[] args) {
String s = getList();
}
private static <T extends List> T getList() {
return (T)new ArrayList();
}
Run Code Online (Sandbox Code Playgroud)
它汇编很好.我的类型安全编译在哪里?该getList()方法与String班级没有任何共同之处.
为什么下面的代码片段会编译?OtherInterface没有扩展,Concrete所以我打赌肾脏,这将无法编译.但确实如此.
public class Test {
public static interface SomeInterface {}
public static interface OtherInterface{}
public static class Concrete implements SomeInterface {
public <T extends Concrete> T getConcrete() {
return null;
}
}
public static void doStuff() {
Concrete c = new Concrete();
OtherInterface iCompile = c.getConcrete();
}
}
Run Code Online (Sandbox Code Playgroud)
另一方面,下一个片段不能编译,这是我所期望的.
public class Test {
public static interface SomeInterface {}
public static class UnrelatedClass{}
public static class Concrete implements SomeInterface {
public <T extends Concrete> T getConcrete() { …Run Code Online (Sandbox Code Playgroud)