引用类型中的有界通配符

Bounded wildcards in reference types

我无法理解以下有关两个 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 我不明白的是,为什么可以从 class String 调用方法,例如startsWith()?为什么我只能将 String 对象传递给 p1.test(),我希望能够为 NumberObject 对象调用它。

作为 p1 的行为,我认为 p2 会,但事实并非如此。我什至无法将 String 对象传递给 p2.test()。这对我来说没有意义,因为我们期望一个对象继承自 String(包括 String)。

我想这可能与我们指定引用类型而不是对象本身的类型有关。但是对象使用什么类型?

当您有 Predicate<? super String> 时,这意味着 Predicate 的实际实现保证能够处理 String 的实例。实现是否将 String 视为 StringCharSequence 甚至 Object 都无关紧要,只要它可以处理类型即可。这为 API 使用 接口提供了灵活性。例如:

void addFilter(Predicate<? super String> filter) { ... }

您可以使用 Predicate<CharSequence>Predicate<Object> 实例调用 addFilter,这对 API 无关紧要。例如:

addFilter((CharSequence cs) -> cs.length() % 2 == 0);

但是,当 API 实际调用 filter 时,它 必须 String 的实例传递给 test .如果允许 API 调用 filter.test(new Object()),则上面传递给 addFilterPredicate<CharSequence> 将失败并显示 ClassCastException.

当你有 Predicate<? extends CharSequence>(使用 CharSequence 因为 String 是最终的 class)你不能调用 test 因为实现可能不会能够处理任何类型的CharSequence。例如:

Predicate<? extends CharSequence> predicate = (String s) -> s.startsWith("...");
predicate.test(new StringBuilder());

A ClassCastException 将被抛出,因为 predicate 的 "real type" 实际上是 Predicate<String>,而不是 Predicate<StringBuilder>。因此编译器拒绝对 test 的调用,因为它不是类型安全的。

我也推荐阅读What is PECS (Producer Extends Consumer Super)?

您为 p1 调用 startsWith 是合法的,即使 p1 是使用下限 ? super String 键入的,因为类型参数被推断为String。 lambda 表达式 s -> s.startsWith("a"); 被推断为 Predicate<String>,将其赋值给 Predicate<? super String>.

类型的变量是合法的

这样编译:

Predicate<String> ps = s -> s.startsWith("a");
Predicate<? super String> p1 = ps;

这不是:

// no "startsWith" on Object
Predicate<? super String> p1 = (Object s) -> s.startsWith("a");

JLS 参考位于 Section 15.27.3、"Type of a Lambda Expression"。

If T is a wildcard-parameterized functional interface type and the lambda expression is implicitly typed, then the ground target type is the non-wildcard parameterization (§9.9) of T.

这里,ground target type是lambda表达式的类型,T是target type,这里是你的下界变量数据类型。这允许编译器将 Predicate<String> 指定为地面目标类型,成为 lambda 表达式的类型。

另请注意,您不能将 superclass 对象传递给 p1.test,因为您可以(并且已经)将 Predicate<String> 分配给 p1 ,需要 String.

p1.test(new Object()); // Error: can't pass something higher than String

至于为什么不能传一个Stringp2.test,当你有上界通配符如? extends String时,这意味着类型参数可以是任意的class 即 String 或子类型。 (编译器忽略 String 在这里是 final 并且不能有 String 的任何子 class。)谓词 p2 可以被分配一个Predicate<SillyString>,假设 SillyStringString 的子 class。但是您不能将 String 传递给可能需要 SillyString.

的方法