如果程序员想要将类型X显式地转换为类型Y,那么语言可以允许它,假设程序员比编译器更了解.
但是,这些规则有什么问题吗?并且,编译器是否符合这些规则? ……这些问题太复杂,不太有趣.
我们应该关心的是演员是否有意义;如果它确实有意义,并且编译器拒绝接受它,没问题,只需解决它.
在你的问题中,有两个地方可以插入通配符,在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和第11例中,代码应该编译,但javac拒绝它.要么规则有问题,要么javac有bug.
让我们看看例如,为什么案例00有意义并且应该编译
void test00(Foo> foo) {
Bar> bar = (Bar>) foo;
}
the question is, could there be a class/interface `X`, such that
Bar <: foo>>
=> Foo> <: foo>>
=> Iterable = Iterable
=> X = String
so the answer is yes, the cast makes sense.
以及为什么案例01不应该编译
Foo> <: foo extends string>>
=> Iterable = Iterable extends String>
=> NO SOLUTION
note that Iterable != Iterable extends String>
和案例11
Foo> <: foo extends iterable string>>
=> Iterable <: iterable extends string>
=> X <: string>
令人惊讶的是,案例01不应该编译,尽管它感觉合情合理.根本问题是,Iterable是可变的,我们应该在它使用的任何地方使用通配符.理想情况下我们应该宣布
class Bar implements Foo>
但如果我们到处都插入通配符,那么生活就是地狱.更好的解决方案是声明站点差异.不确定Java是否会在我们退休之前添加该功能.