为什么 javac
在此代码示例中不会因类型错误而中止
import java.util.List;
public class StaticMethodWithBoundedReturnType {
static class Foo {
}
static class Bar extends Foo {
}
static <F extends Foo> F getFoo(String string) {
…
}
public static void main(String[] args) {
// Compiles without error, even though List does not extend Foo.
List<Integer> list = getFoo("baz");
}
}
显然 List
永远不可能是 Foo
的子类型。即使存在 List
的子类型,它会以某种方式扩展 Foo
,然后在 的调用站点分配 list
getFoo()
应该无效。我知道类型删除的存在。但是 javac
不应该能够看到 list
的类型不满足有界类型约束 extends Foo
并因此无法编译类型错误?
为什么 javac
无法对以有界类型参数作为返回类型的静态方法的调用站点进行类型检查?
看起来我可以通过以下轻微修改获得类型安全:
import java.util.List;
public class StaticMethodWithBoundedReturnType {
static class Foo {
}
static class Bar extends Foo {
}
static <F extends Foo> F getFoo(String string, Class<F> clazz) {
…
}
public static void main(String[] args) {
// Does not compile \o/
List<Integer> list = getFoo("baz", List.class);
}
}
但这需要将 Class
参数添加到 getFoo() 中,该参数根本不在方法主体中使用。有没有更好的方法来实现类型安全?
要理解这一点,我们需要理解以下内容的实际含义:
static <F extends Foo> F getFoo(String string) {
return null;
}
也就是说 getFoo
返回必须从进行调用的上下文中推断出的某种类型的值。此外,它还规定了推断类型必须是 Foo
的子类型的约束。 .
自 null
可分配给所有可能的引用类型,适合作为返回值。事实上,这是唯一可能返回的可能。
为了说明,请尝试以下变体:
import java.util.List;
public class StaticMethodWithBoundedReturnType {
static class Foo {
}
static class Bar extends Foo {
}
static <F extends Foo> F getFoo(String string) {
return new Bar();
}
public static void main(String[] args) {
// Compiles without error, even though List does not extend Foo.
List<Integer> list = getFoo("baz");
}
}
这会产生编译错误
StaticMethodWithBoundedReturnType.java:11: error: incompatible types:
Bar cannot be converted to F
return new Bar();
^
您可能会问:为什么 Bar 与 F 不兼容?。
答案:因为F
代表( R & ? extends Foo )
其中 R
是 getFoo
结果的类型被分配给。并且,在 getFoo
内重新不知道什么R
将。 (事实上,它可以有很多不同的类型!)
简而言之,类型签名
<F extends Foo> F getFoo(String string)
有问题。但是,考虑一下:
static <F extends Foo> F getFoo(Class<F> clazz) {
return class.newInstance();
}
这是合法的,并且将返回一个满足运行时类型安全(本地)的值。但是如果您尝试将其分配给 List<Integer>
,您将然后得到预期的编译错误。 :
StaticMethodWithBoundedReturnType.java:16: error: incompatible types:
inference variable F has incompatible bounds
List<Integer> list = getFoo(Bar.class);
^
equality constraints: Bar
upper bounds: List<Integer>,Foo
where F is a type-variable:
F extends Foo declared in method <F>getFoo(Class<F>)
1 error
回到示例,考虑调用:
List<Integer> list = getFoo("baz");
这是合法的,因为结果的推断类型是交集类型 (List<Integer> & ? extends Foo)
.事实上,这种交集类型是可以实现的;例如作为
class Baz extends Bar implements List<Integer> { /* list methods */ }
(我们程序中的这个 Baz
类中没有实现这一事实并不重要。可能有一个。)
所以,我们可以编译程序了。当我们执行它时,list
将被分配null
,这不是运行时类型违规。