为什么Predicate <! - ? super SomeClass - >不适用于Object?

时间:2018-05-09 08:26:46

标签: java generics

假设我们有一个声明为Predicate<? super SomeClass>的谓词。我天真地希望它适用于层次结构中SomeClass的任何超类,包括Object

但是,此谓词不适用于Object。我收到以下错误:

  

谓词类型中的方法测试(捕获超级SomeClass的#3)不适用于参数(对象)

Demo

为什么Predicate<? super SomeClass>不适用于Object的实例?

代码:

import java.util.*;
import java.lang.*;
import java.io.*;
import java.net.URL;
import java.util.function.Predicate;


/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Predicate<? super URL> p = u -> u.getFile().isEmpty();
        p.test(new Object());
    }
}

5 个答案:

答案 0 :(得分:17)

对于Predicate<? super SomeClass>变量,您可以指定Predicate<SomeClass>个实例或Predicate<Object>个实例。

但是,您无法将Object传递给test()的{​​{1}}方法。您只能传递Predicate<SomeClass>个实例。

因此,您无法将SomeClass传递给Object的{​​{1}}方法

请考虑以下事项:

test()

Predicate<? super SomeClass>指的是Predicate<URL> p1 = u -> u.getFile().isEmpty(); Predicate<? super URL> p2 = p1; ,因此您无法将p2传递给其Predicate<URL>方法。

换句话说,为了让编译器接受new Object(),它必须对可以分配给{{1}的任何 test()有效变量。由于p.test(new Object()) Predicate可以分配给该变量,并且其Predicate<? super URL> p方法不能接受Predicate<URL>Predicate不能被编译器接受。

BTW,在您的具体示例中,您创建的是test()Object是最终的类。因此,您应该将其声明为:

p.test(new Object())

没有理由Predicate<URL>URL

答案 1 :(得分:1)

Predicate 已经一种类型。谓词接受的类型无法在您尝试调用时确定。它有一个类型,那个类型是X的一些超类;它只是未知。

如果我有Predicate<Collection>,则可以引用Predicate<? super List>。但这并不意味着它会接受任何Object? extends X泛型类型并不意味着它将接受与给定约束匹配的任何。这意味着它是某种与给定约束匹配的未知类型的谓词。

Predicate<? super List> predicate = Collection::isEmpty;
predicate.test(new Object()); // Should this be valid? Clearly not

答案 2 :(得分:1)

考虑更广泛的例子:

Predicate<? super Integer> p;

在这种情况下,以下任何作业都有效,对吧?

p = (Predicate<Integer>) i -> true; // but, only Integer can be applied
p = (Predicate<Number>)  n -> true; // Number and its sub-classes (Integer, Double...)
p = (Predicate<Object>)  o -> true; // any type

所以,如果它最终是:

Predicate<? super Integer> p  = (Predicate<Integer>) i -> true;

然后,当然,NumberObject 都不能应用于谓词。

为了保证类型安全,编译器只允许那些对Predicate<? super Integer>的任何可能赋值有效的类型 - 所以,在这个特定的例子中只有Integer

将下限从Integer更改为Number分别扩展了边界:

Predicate<? super Number> p  = (Predicate<Number>) n -> true;

现在,谓词可以应用于Number及其任何子类:IntegerDouble等。

综述

可以应用于Predicate<? super SomeClass>而不破坏类型安全保证的唯一类型:下限本身及其子类。

答案 3 :(得分:1)

使用泛型时,java教程页面提供了有关upper / lower有界通配符的一些很好的信息。

它还提供了一些guidelines来使用通配符,这可以帮助决定你应该使用哪个通配符:

  

&#34; In&#34;变量       &#34; in&#34;变量将数据提供给代码。想象一下带有两个参数的复制方法:copy(src,dest)。 src参数提供了   要复制的数据,因此它是&#34; in&#34;参数。一个&#34; Out&#34;变量       一个&#34; out&#34;变量保存数据供其他地方使用。在复制示例中,copy(src,dest),dest参数接受数据,因此它是   &#34;列&#34;参数。

     

当然,有些变量同时用于&#34; in&#34;和&#34; out&#34;目的 -   指南中也讨论了这种情况。

     

您可以使用&#34; in&#34;和&#34; out&#34;决定是否使用时的原则   通配符以及适合的通配符类型。下列   列表提供了遵循的准则:通配符指南:

An "in" variable is defined with an upper bounded wildcard, using the extends keyword.
An "out" variable is defined with a lower bounded wildcard, using the super keyword.
In the case where the "in" variable can be accessed using methods defined in the Object class, use an unbounded wildcard.
In the case where the code needs to access the variable as both an "in" and an "out" variable, do not use a wildcard.
     

这些指南不适用于方法的返回类型。用一个   应该避免使用通配符作为返回类型,因为它会强制执行   程序员使用代码来处理通配符。

答案 4 :(得分:0)

这是一个非常复杂的主题。这些? extends T? super T声明应该在类之间创建“匹配”。

如果某个类定义了一个采用Consumer的方法,比如Iterable<T>.foreach(),则会定义此消费者接受可以采用T的所有内容。因此,它defines it as forEach(Consumer<? super T> action)。为什么?因为可以使用Iterator<Integer>甚至foreach()来调用Consumer<Number>的{​​{1}}。如果没有Consumer<Object>,这是不可能的。

OTOH,如果一个类定义一个采用? super T的方法,比如Supplier,它会定义该供应商提供addFrom的所有内容。因此,它将其定义为T。为什么?因为可以使用addFrom(Supplier<? extends T> supplier)ThisClass<Number>来调用addFrom()的{​​{1}}。如果没有Supplier<Integer>,这是不可能的。

对于后者可能是一个更好的例子:List<E>.addAll()需要Supplier<Double>。这样就可以将? extends T的元素添加到Collection<? extends E>,但反之亦然。