java - 为什么 javac 不能对带有有界类型参数作为返回类型的静态方法的调用站点进行类型检查?
问题描述
为什么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 )
whereR
是结果getFoo
分配给的类型。而且,在getFoo
re 中无法知道R
会发生什么。(事实上,它可以有很多不同的类型!)
简而言之,类型签名
<F extends Foo> F getFoo(String string)
是有问题的。但是,考虑一下:
static <F extends Foo> F getFoo(Class<F> clazz) {
return class.newInstance();
}
这是合法的,并且将返回一个满足运行时类型安全性(本地)的值。但是,如果您尝试将其分配给 a ,您将得到预期的编译错误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
,这不是运行时类型违规。
推荐阅读
- c++ - 无法将结构的项目复制到另一个结构中
- javascript - 是否可以使文本在画布中从左到右消失?
- coq - 如何在 Fixpoint 定义 Coq 中创建条件
- c++ - c++ std::list 编译问题?与 list.end() 和 list.end() 相关 - 1
- batch-file - 如何过滤确切的文件扩展名?
- c# - 使用 WebBrowser 复制网站文本失败
- pandas - 如何使用没有标题(csv 文件)的数据创建 tf.feature_columns?
- dart - 将 $ 替换为字符串中的 \$ 以用于 Dart 消费(非原始)
- python-3.x - 如何让我的烧瓶响应显示在 XHR 过滤器中?
- c - 如何从未知的数字中打印 2 个最小的数字