我看到以下代码的Eclipse和javac之间的行为存在重大差异:
public class TestIncompatibleTypes {
private static <V> void libraryMethod(Class<? extends List<V>> in) {}
public static void main(String[] args) {
// Eclipse warns about 'Unchecked cast'
// Maven fails with 'incompatible types'
Class<? extends List<String>> l = (Class<? extends List<String>>) ArrayList.class;
libraryMethod(l);
}
}
Eclipse 为上述代码发出"未选中的强制转换"警告,但它已成功编译。Javac 生成错误:
$ java -version
openjdk version "1.8.0_72-internal"
OpenJDK Runtime Environment (build 1.8.0_72-internal-b15)
OpenJDK 64-Bit Server VM (build 25.72-b15, mixed mode)
$ javac -version
javac 1.8.0_72-internal
$ javac -source 8 TestIncompatibleTypes.java
TestIncompatibleTypes.java:13: error: incompatible types: Class<ArrayList> cannot be converted to Class<? extends List<String>>
Class<? extends List<String>> l = (Class<? extends List<String>>) ArrayList.class;
^
1 error
谁能解释为什么javac不允许演员阵容?我的理解是,在类型擦除后,它们应该完全等效。
有没有解决方法可以让它在两个编译器上编译?
有没有解决方法可以让它在两个编译器上编译?
Class<? extends List<String>> clazz;
// casting to interim type
clazz = (Class<? extends List<String>>) (Class<? extends List>) ArrayList.class;
// raw types .......
clazz = (Class) ArrayList.class;
然而!这段代码值得解释为什么它有效以及何时可能不安全:
-
java.util.ArrayList
对其类型变量没有特定的约束。 -
java.lang.Class
是不可变的,只能用于创建新实例。
如果您使用除 ArrayList
和 Class
之外的其他类型,则相同的转换可能会通过例如丢弃重要信息来造成堆污染:
class IntegerList extends ArrayList<Integer> {}
// now we can create a new instance and put String in a List<Integer>
Class<? extends List<String>> clazz =
(Class<? extends List<String>>) (Class<? extends List>) IntegerList.class;
(我还要提醒一下,使用ArrayList.class
仍然有可能造成堆污染,我只是没有认真考虑过。
在Java 8中,我们应该更喜欢lambdas而不是反射来创建实例,这避免了整个问题:
static <V> void libraryMethod(Supplier<? extends List<V>> in) {}
void somewhere() {
Supplier<? extends List<String>> supplier = ArrayList::new;
libraryMethod(supplier);
}
当然,如果您由于某种原因不能使用它,那么您就有点陷入了偶尔与Class
一起做笨拙的事情。
谁能解释为什么javac不允许演员阵容?
Java 不允许"横向"转换,例如:
Number n = ...;
String s = (Number) n; // compiler error
我在这里和这里解释了它如何适用于原始类型参数。
简短的解释是,存在这样的子类型关系:
Class<? extends List>
╱ ╲
Class<? extends List<String>> Class<ArrayList>
两者都是Class<? extends List>
的亚型,既不是亚型也不是另一种的超型。
看起来您正在向Class<? extends List<String>>
投Class<ArrayList>>
,但ArrayList
类没有这种泛型。我不确定您是否需要<String>
通用的Class<? extends List>