转换泛型 class 在 eclipse 中是警告,但在 javac 中是错误
Casting generic class is warning in eclipse but error in javac
对于以下代码,我发现 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 对上述代码发出 'unchecked cast' 警告,但编译成功。 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<ArrayList>>
转换为 Class<? extends List<String>>
,但是 ArrayList
class 没有那种泛型。我不确定您的 Class<? extends List>
是否需要 <String>
通用
Is there a workaround to get this to compile on both compilers?
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
做一些笨拙的事情。
Can anyone explain why javac disallows the cast?
Java 不允许 "sideways" 转换,例如:
Number n = ...;
String s = (Number) n; // compiler error
我解释了原始类型参数的工作原理 and here。
简短的解释是存在这样的子类型关系:
Class<? extends List>
╱ ╲
Class<? extends List<String>> Class<ArrayList>
两者都是 Class<? extends List>
的子类型,并且两者都不是另一个的子类型或超类型。
对于以下代码,我发现 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 对上述代码发出 'unchecked cast' 警告,但编译成功。 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<ArrayList>>
转换为 Class<? extends List<String>>
,但是 ArrayList
class 没有那种泛型。我不确定您的 Class<? extends List>
<String>
通用
Is there a workaround to get this to compile on both compilers?
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
做一些笨拙的事情。
Can anyone explain why javac disallows the cast?
Java 不允许 "sideways" 转换,例如:
Number n = ...;
String s = (Number) n; // compiler error
我解释了原始类型参数的工作原理
简短的解释是存在这样的子类型关系:
Class<? extends List>
╱ ╲
Class<? extends List<String>> Class<ArrayList>
两者都是 Class<? extends List>
的子类型,并且两者都不是另一个的子类型或超类型。