为什么Predicate 不适用于Object?

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

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

Predicate类型中的方法测试(捕获超级SomeClass的#3)不适用于参数(Object)

演示 。

为什么Predicate Predicate不适用于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 p = u -> u.getFile().isEmpty(); p.test(new Object()); } } 

对于Predicate Predicate variable,您可以分配Predicate实例或Predicate实例。

但是,您不能将Object传递给Predicatetest()方法。 您只能传递SomeClass实例。

因此,您无法将Object传递给Predicate test()方法Predicate Predicate

考虑以下:

 Predicate p1 = u -> u.getFile().isEmpty(); Predicate p2 = p1; 

p2指的是Predicate ,因此您无法将new Object()传递给其test()方法。

换句话说,为了让编译器接受p.test(new Object()) ,它必须对任何可以分配给Predicate p Predicate p变量。 由于Predicate Predicate可以分配给该变量,并且其test()方法不能接受Object ,因此编译器不能接受p.test(new Object())

顺便说一句,在您的具体示例中,您正在创建Predicate ,而URL是最终类。 因此,您应该将其声明为:

 Predicate p = u -> u.getFile().isEmpty(); 

没有理由? super ? super还是? extends ? extends

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

如果我有一个Predicate ,那么可以引用Predicate Predicate 。 但这并不意味着它会接受任何Object 。 的? extends X ? extends Xgenerics类型并不意味着它将接受与给定约束匹配的任何内容 。 这意味着它是某种与给定约束匹配的未知类型的谓词。

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

在使用generics时,java教程页面提供了有关上 / 下有界通配符的一些很好的信息。

它还提供了一些使用通配符的指南 ,可以帮助确定应该使用哪个通配符:

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

当然,一些变量既用于“进入”又用于“出”目的 – 该方案也在指南中解决。

在决定是否使用通配符以及适合使用哪种类型的通配符时,可以使用“in”和“out”原则。 以下列表提供了遵循的准则:通配符指南:

 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. 

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

考虑更广泛的例子:

 Predicate p; 

在这种情况下,以下任何一项任务都是有效的,对吧?

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

所以,如果最终是:

 Predicate p = (Predicate) i -> true; 

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

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

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

 Predicate p = (Predicate) n -> true; 

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

总结

唯一可以应用于Predicate Predicate不破坏类型安全保证:下限本身及其子类。

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

如果一个类定义了一个接受Consumer的方法,比如像Iterable.foreach()这样的东西,它会定义这个消费者接受所有可以接受T 。 因此,它将其定义为forEach(Consumer action) 。 为什么? 因为可以使用Consumer甚至Consumer调用Iteratorforeach() 。 如果没有这个,这是不可能的? super T ? super T

OTOH,如果一个类定义了一个采用Supplier的方法,比如像addFrom这样的东西,它定义这个供应商来提供T所有东西。 因此,它将其定义为addFrom(Supplier supplier) 。 为什么? 因为可以使用SupplierSupplier调用ThisClassaddFrom() 。 如果没有这个,这是不可能的? extends T ? extends T

也许是后者的一个更好的例子: List.addAll()采用Collection Collection 。 这使得可以将List的元素添加到List ,但反之亦然。