I have trouble understanding following code about two Predicate objects. The first one uses a lower bounded wildcard, the second a upper bounded.
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?)
What I don't understand about p1 is, why is it possible to call methods from the class String, e.g. startsWith()? Why can I only pass String objects into p1.test(), I expected to be able to call it for Number and Object objects as well.
As p1 behaves I thought p2 would, but this isn't the case. I can't even pass a String object into p2.test(). This doesn't makes sense to me, because we expect an object which inherits from String (including String).
I think it maybe has something to do with the fact, that we specify the reference type rather than the type of the object itself. But what type is then used for the object?
It's legal for you to call startsWith for p1, even though p1 is typed with a lower bound ? super String, because the type argument is inferred to be String. The lambda expression s -> s.startsWith("a"); is inferred to be a Predicate<String>, which is legal to assign to a variable of type Predicate<? super String>.
This compiles:
Predicate<String> ps = s -> s.startsWith("a");
Predicate<? super String> p1 = ps;
This does not:
// no "startsWith" on Object
Predicate<? super String> p1 = (Object s) -> s.startsWith("a");
The JLS reference is in 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.
Here, the ground target type is the type of the lambda expression, and T is the target type, which here is your lower bound variable datatype. This allows the compiler to assign Predicate<String> as the ground target type, which becomes the type of the lambda expression.
Also notice that you can't pass a superclass object to p1.test, because you could (and you already have) assigned a Predicate<String> to p1, which takes a String.
p1.test(new Object()); // Error: can't pass something higher than String
As for why you can't pass a String to p2.test, when you have an upper bounded wildcard such as ? extends String, that means that the type parameter can be any class that is either String or a subtype. (The compiler ignores that String is final here and there can't be any subclasses of String.) The predicate p2 could be assigned a Predicate<SillyString>, assuming SillyString is a subclass of String. But you can't pass a String to a method that could expect a SillyString.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With