Java 8谓词 - 为什么不能加入通配符泛型谓词?

sta*_*032 7 java generics java-8

请考虑以下代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

它没有编译,给出错误:

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>
Run Code Online (Sandbox Code Playgroud)

看起来我们不能用"或"或"和"之类的逻辑运算符加入这样的谓词,但为什么Java不能处理它们呢?

它用简单的谓词编译Predicate<TestObject>,但不是用Predicate<? super TestObject>Predicate<? extends TestObject>.

Mal*_*wig 7

您可能知道谓词是兼容的,但编译器不兼容.

想象一下这个例子:

Predicate<? extends Collection<Object>> p1 = (Set<Object> s) -> s.isEmpty();
Predicate<? extends Collection<Object>> p2 = (List<Object> l) -> l.get(0) != null;
Run Code Online (Sandbox Code Playgroud)

我们的开发人员可以看到第一个谓词可以在技术上处理所有集合,而第二个谓词只能处理列表.但是想象一下,谓词在其他地方被初始化,或者在此期间会被改变.编译器无法确定谓词对象的集合类型.因此,您根本无法使用它们:

Set<Object> set = new HashSet<>();
List<Object> list = new ArrayList<>();
p1.test(set);
p2.test(list);
p1.test(list);
p2.test(set);
Run Code Online (Sandbox Code Playgroud)

所有这些电话将不能编译,因为编译器不能说落后于实际的对象是否p1p2CAN正是这些类型的集合.这就是? extends Collection<>:你知道它是一个特定的子类型,但你无法告诉编译器究竟是哪一个.


用一个更简单的例子来说明这一点:

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
Run Code Online (Sandbox Code Playgroud)

您不能将任何一种水果添加到您不知道的类型的篮子中.它实际上可以是一个接受所有水果的篮子,但它可能是一个纯苹果篮子,橙色支持,甚至是你甚至还不知道的香蕉篮子.

在IDE中尝试:

List<? extends String> l1 = new ArrayList<>();
List<? extends String> l2 = new ArrayList<>();
l1.addAll(l2);
Run Code Online (Sandbox Code Playgroud)

Eclipse告诉我:

方法addAll(Collection1-of?extends String>)类型为List <capture#1 -of?extends String>不适用于参数(List <capture#2 -of?extends String>)

注意不同的类型:addAll期望集合capture#1,l2是集合capture#2.

  • 值得一提的是,虽然像谓词<?extends Collection <Object >> p1 = Collection :: isEmpty;`显然用一个可以处理各种集合的谓词初始化`p1`,没有什么能阻止我们在稍后的时间执行像`p1 = p2;`这样的赋值,因为这些变量的类型是兼容的,都指的是未知的集合子类型的谓词.毕竟,这是使用比我们分配给它们的值更广泛的类型声明变量的主要原因,在我们稍后分配的值的实际类型中获得灵活性. (4认同)