2016-04-21 50 views
2

我看到在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不允许演员?我的理解是,类型擦除后它们应该完全等效。

是否有解决方法让这两个编译器上编译?

+0

只要你使用未经检查的铸造,那么'Class <?扩展列表> l =(Class)ArrayList.class;'? – khelwood

回答

3

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是不可变的,只能用于创建新的实例。

如果您使用除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中,我们应该更喜欢lambda表达式反射的实例创建,避免了这个问题,整体:

static <V> void libraryMethod(Supplier<? extends List<V>> in) {} 
void somewhere() { 
    Supplier<? extends List<String>> supplier = ArrayList::new; 
    libraryMethod(supplier); 
} 

当然,如果你不能使用这个出于某种原因,那么你是一个有点卡住做偶尔与kludgy事情。

Can anyone explain why javac disallows the cast?

Java不允许 “侧身” 铸就,例如:

Number n = ...; 
String s = (Number) n; // compiler error 

我解释这是如何工作的原始类型参数herehere

短的解释是,存在这样的子类型关系:

  Class<? extends List> 
      ╱     ╲ 
Class<? extends List<String>> Class<ArrayList> 

两者都是Class<? extends List>亚型和没有一个是一个亚型,也没有其他的超类型。

+0

优秀的响应。你的分型树使错误的原因很清楚,尽管考虑到演员阵容本身具有风险(至少从编译器的角度来看),禁止横向演员似乎很奇怪。 – Quantum7

+0

横向转换不能编译为非泛型类型,因为您可以确定转换总是失败('null'除外)。诚然,泛型类型并非总是如此。 – Radiodef

+0

非常好的答案,非常感谢。 –

1

看起来您正在投射Class<ArrayList>>Class<? extends List<String>>,但ArrayList类没有那种类型。我不知道,如果你需要的<String>一般为您Class<? extends List>

+2

@JoopEggen试试看看它是否有效。它适用于'libraryMethod(Class <?extends List>)',但它不适用于'libraryMethod(Class <?extends List >)' – khelwood