[英]Java 8 Predicate - why can't wildcard generics predicates be joined?
Consider the following code: 请考虑以下代码:
public class Main {
private static Predicate<? extends TestObject> predicate = testObject -> true;
private static Predicate<? extends TestObject> predicate1 = testObject -> true;
public static void main( String[] args ) {
List<TestObject> objects = Lists.newArrayList( new TestObject(), new TestObject() );
objects.stream().filter( predicate.or( predicate1 ) ).findFirst();
}
}
It doesn't compile, giving the error: 它没有编译,给出错误:
Error:(17, 48) java: incompatible types: java.util.function.Predicate<capture#1 of ? extends test.test.TestObject> cannot be converted to java.util.function.Predicate<? super capture#2 of ? extends test.test.TestObject>
It seems like we can't join such predicates with logical operators like "or" or "and", but why can't Java deal with them? 看起来我们不能用“或”或“和”这样的逻辑运算符加入这样的谓词,但为什么Java不能处理它们呢?
It compiles with simple predicates like Predicate<TestObject>
, but not with Predicate<? super TestObject>
它使用像
Predicate<TestObject>
这样的简单谓词进行编译,但不能用Predicate<? super TestObject>
Predicate<? super TestObject>
or Predicate<? extends TestObject>
Predicate<? super TestObject>
或Predicate<? extends TestObject>
Predicate<? extends TestObject>
. Predicate<? extends TestObject>
。
You might know that the predicates are compatible, but the compiler does not. 您可能知道谓词是兼容的,但编译器不兼容。
Imagine this example: 想象一下这个例子:
Predicate<? extends Collection<Object>> p1 = (Set<Object> s) -> s.isEmpty();
Predicate<? extends Collection<Object>> p2 = (List<Object> l) -> l.get(0) != null;
We developers can see that the first predicate could technically handle all collections, while the second one can handle only lists. 我们的开发人员可以看到第一个谓词可以在技术上处理所有集合,而第二个谓词只能处理列表。 But imagine the predicates were initialized somewhere else or would be changed in the meantime.
但是想象一下,谓词在其他地方被初始化,或者在此期间会被改变。 The compiler cannot know for sure which type of collections the predicate objects were made for.
编译器无法确定谓词对象的集合类型。 As a result, you cannot use them at all:
因此,您根本无法使用它们:
Set<Object> set = new HashSet<>();
List<Object> list = new ArrayList<>();
p1.test(set);
p2.test(list);
p1.test(list);
p2.test(set);
All these calls won't compile, because the compiler cannot say whether the actual objects behind p1
and p2
can are exactly for those types of collections. 所有这些调用都不会编译,因为编译器无法确定
p1
和p2
后面的实际对象是否完全适用于那些类型的集合。 That is the meaning of ? extends Collection<>
那是
? extends Collection<>
意思? extends Collection<>
? extends Collection<>
: You know it is a specific sub type, but you cannot tell the compiler which one exactly. ? extends Collection<>
:你知道它是一个特定的子类型,但你无法告诉编译器究竟是哪一个。
To illustrate this with a simpler example: 用一个更简单的例子来说明这一点:
Collection<Apple> appleBasket = ...;
appleBasket.add(new Apple()); // works
appleBasket.add(new Orange()); // does not work (obviously)
Collection<Fruit> mixedFruitBasket = ...;
mixedFruitBasket.add(new Apple()); // works
mixedFruitBasket.add(new Orange()); // works
// Now the tricky part
Collection<? extends Fruit> unknownButPureFruitBasket = ...;
unknownButPureFruitBasket.add(new Apple()); // does not work
unknownButPureFruitBasket.add(new Orange()); // does not work
You cannot add any one fruit to a basket whose type you don't know. 您不能将任何一种水果添加到您不知道的类型的篮子中。 It could in fact be a basket that accepts all fruit, but it could be a pure Apple basket, or Orange backet, or even a Banana basket that you do not even know of yet.
它实际上可以是一个接受所有水果的篮子,但它可能是一个纯苹果篮子,橙色支持,甚至是你甚至还不知道的香蕉篮子。
Try it in your IDE: 在IDE中尝试:
List<? extends String> l1 = new ArrayList<>();
List<? extends String> l2 = new ArrayList<>();
l1.addAll(l2);
Eclipse tells me: Eclipse告诉我:
The method addAll(Collection1-of ? extends String>) in the type List<capture# 1 -of ?
方法addAll(Collection1-of?extends String>)在List <capture# 1 -of?中。 extends String> is not applicable for the arguments (List<capture# 2 -of ? extends String>)
extends String>不适用于参数(List <capture# 2 -of?extends String>)
Note the different types: addAll
expects a collection of capture#1
, l2
is a collection of capture#2
. 注意不同的类型:
addAll
需要capture#1
的集合, l2
是capture#2
的集合。
filter
requires a Predicate<? super TestObject>
filter
需要Predicate<? super TestObject>
Predicate<? super TestObject>
, not Predicate<? extends TestObject>
Predicate<? super TestObject>
,而不是Predicate<? extends TestObject>
Predicate<? extends TestObject>
. Predicate<? extends TestObject>
。
Why super TestObject
? 为什么
super TestObject
? Because the TestObject here is an input parameter, a consumer . 因为这里的TestObject是一个输入参数,一个消费者 。 According to the PECS rule, it should be marked super.
根据PECS规则,它应该标记为超级。
Predicate<? extends TestObject>
Predicate<? extends TestObject>
is completely different from Predicate<? super TestObject>
Predicate<? extends TestObject>
与Predicate<? super TestObject>
完全不同Predicate<? super TestObject>
Predicate<? super TestObject>
. Predicate<? super TestObject>
。 The former can accept all subclasses of TestObject
and TestObject
. 前者可以接受
TestObject
和TestObject
所有子类。 The latter can accept all superclasses of TestObject
and TestObject
. 后者可以接受
TestObject
和TestObject
所有超类。 Obviously they are different and non-compatible. 显然它们是不同的,不兼容的。
This is really not about how Java handles predicates, but it's simply a question of generics. 这实际上不是关于Java如何处理谓词,而只是一个泛型问题。
In short, the answer is that predicate
and predicate1
aren't necessarily compatible. 简而言之,答案是
predicate
和predicate1
不一定兼容。 The type of the parameter to or
is Predicate<? super T>
所述类型的参数的来
or
是Predicate<? super T>
Predicate<? super T>
, assuming that T
in the two Predicate
objects is the same concrete type argument. Predicate<? super T>
,假设两个Predicate
对象中的T
是相同的具体类型参数。 But this is not the case. 但这种情况并非如此。
To illustrate the problem, assume: 为了说明问题,假设:
class TestObject2 extends TestObject {
boolean isTrue() {
return true;
}
}
class TestObject3 extends TestObject {
boolean isTrue3() {
return true;
}
}
And (note that the declared types didn't change, but the actual lambda expressions did): 并且(注意声明的类型没有改变,但实际的lambda表达式确实如此):
private static Predicate<? extends TestObject> predicate =
(TestObject2 testObject) -> testObject.isTrue();
private static Predicate<? extends TestObject> predicate1 =
(TestObject3 testObject) -> testObject.isTrue3();
In this example, or
would be getting the wrong value ( predicate
accepts a TestObject2
object, but in this case, it could be called with a TestObject3
object). 在此示例中,
or
将获取错误的值( predicate
接受TestObject2
对象,但在这种情况下,可以使用TestObject3
对象调用它)。
How do you think predicate1
would work in this filter
? 您如何看待
predicate1
在此filter
?
Stream.of(new TestObject2(), new TestObject2()).filter(predicate.or(predicate1));
The fact is that the compiler is guaranteeing that Predicate<? extends TestObject>
事实是,编译器保证
Predicate<? extends TestObject>
Predicate<? extends TestObject>
is compatible with both Predicate<TestObject2>
and Predicate<TestObject3>
(and that's as per the law of generics) Predicate<? extends TestObject>
兼容Predicate<TestObject2>
和Predicate<TestObject3>
(根据泛型法则)
This is PECS yet again, since you consume some TestObject
via the Predicate
, you would need ? super TestObject
这又是PECS,因为你通过
Predicate
消耗了一些TestObject
,你需要? super TestObject
? super TestObject
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.