我很难理解以下有关两个Predicate
对象的代码。第一个使用下界通配符,第二个使用上界通配符。
Predicate<? super String> p1 = s -> s.startsWith("a"); // why can I call startsWith()?
Predicate<? extends String> p2 = s -> s.startsWith("a");
p1.test("a"); // works
p2.test("a"); // doesn't work (why?)
我对p1
不了解,为什么可以从类String
调用方法,例如startsWith()
?为什么我只能将String
个对象传递到p1.test()
中,我希望能够同时为Number
和Object
对象调用它。
按照p1
的行为,我以为p2
可以,但是事实并非如此。我什至无法将String
对象传递到p2.test()
中。这对我来说没有任何意义,因为我们希望有一个对象继承自String
(包括String
)。
我认为这可能与以下事实有关:我们指定引用类型而不是对象本身的类型。但是该对象使用什么类型?
答案 0 :(得分:5)
即使startsWith
的输入下限为p1
,也可以为p1
调用? super String
,因为推断类型参数为{{ 1}}。 λ表达式String
推断为s -> s.startsWith("a");
,可以合法地将其分配给类型为Predicate<String>
的变量。
它将编译:
Predicate<? super String>
这不是:
Predicate<String> ps = s -> s.startsWith("a");
Predicate<? super String> p1 = ps;
JLS参考位于Section 15.27.3“ Lambda表达式的类型”中。
如果T是通配符参数化的功能接口类型,并且lambda表达式是隐式键入的,则基本目标类型是T的非通配符参数化(第9.9节)。
在这里,基本目标类型是lambda表达式的类型,而// no "startsWith" on Object
Predicate<? super String> p1 = (Object s) -> s.startsWith("a");
是目标类型,这是您的下界变量数据类型。这样,编译器就可以将T
分配为基本目标类型,该目标类型将成为lambda表达式的类型。
还请注意,您无法将超类对象传递给Predicate<String>
,因为您可以(并且已经)将p1.test
分配给Predicate<String>
,这需要{{1 }}。
p1
关于为什么不能将String
传递给p1.test(new Object()); // Error: can't pass something higher than String
的原因,当您使用String
这样的上限通配符时,这意味着type参数可以是任何是p2.test
或子类型。 (编译器会忽略? extends String
在此处是String
,并且String
不会有任何子类。)可以为谓词final
分配一个String
,假设p2
是Predicate<SillyString>
的子类。但是您不能将SillyString
传递给可能期望String
的方法。
答案 1 :(得分:2)
当您拥有Predicate<? super String>
时,这意味着Predicate
的实际实现可以保证能够处理String
的实例。实现是否将String
视为String
或CharSequence
或什至Object
无关紧要,只要它可以处理类型即可。这在使用接口的API中提供了灵活性。例如:
void addFilter(Predicate<? super String> filter) { ... }
您可以使用addFilter
或Predicate<CharSequence>
实例调用Predicate<Object>
,这与API无关。例如:
addFilter((CharSequence cs) -> cs.length() % 2 == 0);
但是,当API实际调用filter
时,它有 将String
的实例传递给test
。如果允许该API调用filter.test(new Object())
,则传递给上述Predicate<CharSequence>
的{{1}}将失败,并返回addFilter
。
当您拥有ClassCastException
时(由于Predicate<? extends CharSequence>
是最终课程,因此请使用CharSequence
),您将无法调用String
,因为该实现可能无法处理任何类型test
中的。例如:
CharSequence
将抛出Predicate<? extends CharSequence> predicate = (String s) -> s.startsWith("...");
predicate.test(new StringBuilder());
,因为ClassCastException
的“实型”实际上是predicate
,而不是Predicate<String>
。因此,编译器拒绝对Predicate<StringBuilder>
的调用,因为它不是类型安全的。