2013-07-17 61 views
6

此代码会导致用javac编译错误(但是,值得注意的是,不使用Eclipse 4.2.2!):为什么在这里不允许转换为“GenericType <?>”?

public interface Foo<T> { 
} 

class Bar<T> implements Foo<Iterable<T>> { 
} 

class Test { 
    void test(Foo<? extends Iterable<? extends String>> foo) { 
     Bar<?> bar = (Bar<?>) foo; 
    } 
} 

从javac的错误是这样的:

Foo.java:9: error: inconvertible types 
     Bar<?> bar = (Bar<?>) foo; 
          ^
    required: Bar<?> 
    found: Foo<CAP#1> 
    where CAP#1 is a fresh type-variable: 
    CAP#1 extends Iterable<? extends String> from capture of ? extends Iterable<? extends String> 

更改强制转换为(Bar) foo (即使用原始类型)允许编译代码,也可以将foo的类型更改为Foo<? extends Iterable<?>>

编辑:欢快的,这个简单的变化使得Eclipse的拒绝,但javac的接受:

void test(Foo<Iterable<String>> foo) { 
    Bar<?> bar = (Bar<?>) foo; 
} 

而且,Eclipse和javac的拒绝此一:

void test(Foo<Iterable<? extends String>> foo) { 
    Bar<?> bar = (Bar<?>) foo; 
} 
+0

希望你永远不必像这样做任何丑陋的铸造,这只是一个好奇的问题... – jahroy

+0

是的只是好奇心。真实生活中的例子实际上是“instanceof Bar”工作但“instanceof Bar ”没有的检查实例。 –

+0

这里有一些提及,http://docs.oracle.com/javase/tutorial/java/generics/capture.html –

回答

2

如果程序员想要明确地将类型X转换成Y类型,则假定程序员比编译器知道的更好,该语言可以允许它。

但是Java中的好人们想要阻止一些明显不可能的演员,例如, (Cat)dog。所以他们有这个致力于这个主题的这个光荣的详细部分 - http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.5.1

但是,这些规则是否有任何问题?而且,编译器是否符合这些规则? ......这些问题太复杂,不太有趣。

我们应该关心的是演员是否有意义;如果它确实有道理,并且编译器拒绝接受它,没有问题,只需解决它。


在你的问题中,有插入通配符,在Foo< >两个地方,并在Iterable< >。在每一个地方,通配符可以

0. None 
    1. Bounded "? extends Something" 
    2. Unbounded "?" 

让我们探索所有的组合,这是我的结论是:

 wildcard#1 wildcard#2  should_compile javac  eclipse 

00 -    -    Y    Y   N 
01 -    ? extends  N    N   N 
02 -    ?    N    N   N 
10 ? extends  -    Y    N   Y 
11 ? extends  ? extends  Y    N   Y 
12 ? extends  ?    Y    Y   Y 
20 ?    -    Y    Y   Y 

should_compile意味着投有道理,还是没有,稍后解释。

如果10 and 11,代码应该编译,但javac拒绝它。规则有问题,或者javac有错误。


让我们来看看,例如,为什么情况00有意义,应编译

void test00(Foo<Iterable<String>> foo) { 
    Bar<?> bar = (Bar<?>) foo; 
} 

the question is, could there be a class/interface `X`, such that 
     Bar<X> <: Foo<Iterable<String>> 
=> Foo<Iterable<X>> <: Foo<Iterable<String>> 
=> Iterable<X> = Iterable<String> 
=> X = String 
so the answer is yes, the cast makes sense. 

,为什么情况01不应该编译

 Foo<Iterable<X>> <: Foo<Iterable<? extends String>> 
=> Iterable<X> = Iterable<? extends String> 
=> NO SOLUTION 
    note that Iterable<String> != Iterable<? extends String> 

和案例11

 Foo<Iterable<X>> <: Foo<? extends Iterable<? extends String>> 
=> Iterable<X> <: Iterable<? extends String> 
=> X <: String 

令人惊讶的是,案件01不应编译,虽然它感觉合理。根本问题是,Iterable是非常有效的,我们应该在它使用的任何地方使用通配符。所以理想情况下,我们应该声明

class Bar<T> implements Foo<Iterable<? extends T>> 

但是如果我们插入通配符到处都是生命。更好的解决方案是声明网站差异。不确定Java是否会在我们全部退休之前添加该功能。

+0

我对案例01感到惊讶。事实上,我很惊讶,这是无效的:“Foo > foo = new Foo >();“ –

+0

Ohhhh哇,好吧,我没有意识到Foo >是一个完整的类型。 –

+0

@TavianBarnes是的,嵌套通配符与顶级通配符是非常不同的:http://stackoverflow.com/questions/3546745/multiple-wildcards-on-a-generic-methods-makes-java-compiler-and-me-非常confu/3547372#3547372 –

4

你的代码是合理的,应该编译:您正在将静态类型Foo缩小为Bar,同时将通用类型? extends Iterable<? extends String>扩展为?,其中顶级?是通配符捕获。这是一个checked cast,它应该在没有警告的情况下编译。

您应该submit an Oracle bug。我试图搜索相应的错误报告,但this old closed ticket是我能找到的最接近的问题(IMO错误数据库非常难以搜索)。当我有机会时我会搜索更多,因为这个javac问题感觉非常熟悉。

相关问题