这看起来很基本,但我不明白为什么我的代码编译不了。
我有一个class Person
,它有一个String name
属性和一个getName()
getter。
我创建一个Employee class extends Person
我有这个方法:
public Predicate<? extends Person> startsA() {
return p -> p.getName().startsWith("A");
}
所以我希望这个方法能处理Person
的所有子类
但这段代码无法编译:
startsA().test(new Employee());
startsA().test(new Person());
我不明白为什么
2条答案
按热度按时间djmepvbi1#
您的代码无法编译的原因是您定义了
startsA()
方法来返回扩展Person的通配符类型的Predicate。通配符类型表示Person的未知子类型,更改
startsA()
方法以返回Predicate<Person>
而不是使用通配符类型:toe950272#
泛型是一个 meta级别删除。你需要站在编译器的Angular 去理解它们。
我经常看到泛型的错误,也是你似乎犯的错误,就是你认为
Predicate<? extends Person>
的意思是“这是一个可以测试任何人的 predicate ”。一个雇员,一个客户-不重要,因此,“无论什么扩展人”的权利。”””但这并不是它的意思。
毕竟,想象一下它的意思。什么是
Predicate<Person>
?一个 predicate 只能专门测试new Person()
而不能测试new Employer()
?Java并不是这样工作的--任何Employer的示例都可以被用作Person,所以这将是非常奇怪的。实际上,
Predicate<Person>
在这里的意思是:可以测试任何人的 predicate 宾语。Predicate<? extends Person>
是什么意思这一点:一个 predicate 可以测试。。我不知道它能检测什么。我所知道的是,不管它是什么,它是Person或其子类型的某种类型。
那这样做有什么意义呢?在这个特定的情况下(对于 predicate ),并不是那么多。让我们想想列表。我们有3种不同的相关类型(记住,Integer和Double都扩展了Number,Number扩展了Object)
List<? extends Number>
List<? super Number>
List<Number>
您可以将
List<Integer>
对象传递给接受List<? extends Number>
的方法。你不能把它传递给一个接受List<Number>
的方法。为什么不呢- Integer可以扩展Number,对吧?啊,但是在泛型中,这是不变的,就像,不,你不能只是用它的子类型替换类型。毕竟,我可以将Double
对象添加到List<Number>
。我可以在列表中添加Number对象,Double对象就是Number对象,QED。但是,将Double对象添加到
List<Integer>
,这并不好。因此,需要一个List<? extends Number>
-尝试一下,你不能**向这样的列表添加东西。出于同样的原因:List<? extends Number>
并不意味着:“一个可以保存数字的列表-任何数字”。恰恰相反。它的意思是:“一个可以容纳...我不知道它能装什么(因此有问号!我所知道的是,无论它持有什么,它都是数字。或Number的任何子类)。鉴于此,list.get(5)
将返回Number
类型的表达式(鉴于我们所知道的一些事情,这是安全的),但您不能向其添加任何内容(除了字面量null
的学术情况,这是所有类型)。特别是对于
Predicate
,Predicate<? extends>
是完全无用的,在任何情况下都不应该出现。Predicate<? super X>
可以非常方便。毕竟,这意味着:“一个 predicate 可以测试..我不知道它能测试什么。我所知道的是,它要么是X,要么是X的某种超级类型。有了这些信息,.test(instanceOfX)
就可以了。