转换泛型类在 eclipse 中是警告,但在 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 为上述代码发出"未选中的强制转换"警告,但它已成功编译。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是不可变的,只能用于创建新实例。

如果您使用除 ArrayListClass 之外的其他类型,则相同的转换可能会通过例如丢弃重要信息来造成堆污染:

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>

相关内容

最新更新