代码之家  ›  专栏  ›  技术社区  ›  staszko032

Java8谓词-为什么不能连接通配符泛型谓词?

  •  7
  • staszko032  · 技术社区  · 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();
        }
    }
    

    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>
    

    它用简单的谓词编译,比如 Predicate<TestObject> Predicate<? super TestObject> Predicate<? extends TestObject>

    4 回复  |  直到 8 年前
        1
  •  7
  •   Malte Hartwig Darren Gunning    8 年前

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

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

    Set<Object> set = new HashSet<>();
    List<Object> list = new ArrayList<>();
    p1.test(set);
    p2.test(list);
    p1.test(list);
    p2.test(set);
    

    所有这些调用都不会编译,因为编译器不能说后面的实际对象 p1 p2 可以完全用于这些类型的集合。这就是 ? 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
    

    你不能把任何一个水果加到你不知道的篮子里。事实上,它可以是一个篮子,接受所有的水果,但它可以是一个纯粹的苹果篮子,或橙色的靠背,甚至香蕉篮子,你甚至不知道。

    List<? extends String> l1 = new ArrayList<>();
    List<? extends String> l2 = new ArrayList<>();
    l1.addAll(l2);
    

    Eclipse告诉我:

    方法addAll(Collection1 of?扩展类型列表中的字符串# 1 -什么?extends String>不适用于参数(List<capture# -什么?扩展字符串(>)

    注意不同的类型: addAll 需要一组 capture#1 , l2 capture#2 .

        2
  •  4
  •   Sweeper    8 年前

    filter 需要 Predicate<? super TestObject> Predicate<? extends TestObject> .

    为什么? super TestObject ? 因为这里的TestObject是一个输入参数 消费者 PECS 规则,它应该被标记为super。

    谓词<?扩展TestObject> 完全不同于 谓词<?超级测试对象> . 前者可以接受所有的子类 TestObject 测试对象 . 后者可以接受所有的超类 测试对象 . 显然,它们是不同的,不相容的。

        3
  •  3
  •   ernest_k Petronella    8 年前

    这实际上并不是关于Java如何处理谓词,而是一个泛型问题。

    简而言之,答案是 predicate predicate1 不一定兼容。要删除的参数的类型 or Predicate<? super T> ,假设 T Predicate 对象是相同的具体类型参数。但事实并非如此。

    class TestObject2 extends TestObject {
        boolean isTrue() {
            return true;
        }
    }
    class TestObject3 extends TestObject {
        boolean isTrue3() {
            return true;
        }
    }
    

    和(注意声明的类型没有改变,但是实际的lambda表达式改变了):

    private static Predicate<? extends TestObject> predicate = 
            (TestObject2 testObject) -> testObject.isTrue();
    private static Predicate<? extends TestObject> predicate1 = 
            (TestObject3 testObject) -> testObject.isTrue3();
    

    谓语 接受 TestObject2 对象,但在本例中,可以使用 TestObject3 对象)。

    你觉得呢 会在这工作吗 filter ?

    Stream.of(new TestObject2(), new TestObject2()).filter(predicate.or(predicate1));
    

    事实是编译器保证 Predicate<? extends TestObject> 两者都兼容 Predicate<TestObject2> Predicate<TestObject3>

        4
  •  1
  •   Eugene    8 年前

    这又是佩奇,因为你 消费 一些 TestObject Predicate ,你需要 ? super TestObject