为什么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 super SomeClass>
Predicate super SomeClass>
variable,您可以分配Predicate
实例或Predicate
实例。
但是,您不能将Object
传递给Predicate
的test()
方法。 您只能传递SomeClass
实例。
因此,您无法将Object
传递给Predicate super SomeClass>
test()
方法Predicate super SomeClass>
Predicate super SomeClass>
考虑以下:
Predicate p1 = u -> u.getFile().isEmpty(); Predicate super URL> p2 = p1;
p2
指的是Predicate
,因此您无法将new Object()
传递给其test()
方法。
换句话说,为了让编译器接受p.test(new Object())
,它必须对任何可以分配给Predicate super URL> p
Predicate super URL> 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 super List>
Predicate super List>
。 但这并不意味着它会接受任何Object
。 的? extends X
? extends X
generics类型并不意味着它将接受与给定约束匹配的任何内容 。 这意味着它是某种与给定约束匹配的未知类型的谓词。
Predicate super List> 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 super Integer> p;
在这种情况下,以下任何一项任务都是有效的,对吧?
p = (Predicate) i -> true; // but, only Integer can be applied p = (Predicate) n -> true; // Number and its sub-classes (Integer, Double...) p = (Predicate
所以,如果最终是:
Predicate super Integer> p = (Predicate) i -> true;
然后,当然, Number
和Object
都不能应用于谓词。
为了保证类型安全,编译器只允许那些对Predicate super Integer>
任何可能赋值有效的类型Predicate super Integer>
Predicate super Integer>
– 所以,在这个特定的例子中只有Integer
。
将下限从Integer
更改为Number
分别扩展边界:
Predicate super Number> p = (Predicate) n -> true;
现在,谓词可以应用于Number
及其任何子类: Integer
, Double
等。
总结
唯一可以应用于Predicate super SomeClass>
Predicate super SomeClass>
不破坏类型安全保证:下限本身及其子类。
这是一个非常复杂的主题。 这些? extends T
? extends T
和? super T
? super T
声明应该在类之间创建“匹配”。
如果一个类定义了一个接受Consumer
的方法,比如像Iterable
这样的东西,它会定义这个消费者接受所有可以接受T
。 因此,它将其定义为forEach(Consumer super T> action)
。 为什么? 因为可以使用Consumer
甚至Consumer
调用Iterator
的foreach()
。 如果没有这个,这是不可能的? super T
? super T
OTOH,如果一个类定义了一个采用Supplier
的方法,比如像addFrom
这样的东西,它定义这个供应商来提供T
所有东西。 因此,它将其定义为addFrom(Supplier extends T> supplier)
。 为什么? 因为可以使用Supplier
或Supplier
调用ThisClass
的addFrom()
。 如果没有这个,这是不可能的? extends T
? extends T
也许是后者的一个更好的例子: List
采用Collection extends E>
Collection extends E>
。 这使得可以将List
的元素添加到List
,但反之亦然。