I accept that due to the lower bounded wildcard, this predicate should not accept a superclass of String without an explicit cast.[1,2] This question is rather about type safety enforcement in the lambda's parameter list. Given the second block fails compilation, why is the first allowed to compile without a warning? It appears that in the first case, despite the lambda's parameter declaration, CharSequence is being cast to String to satisfy predicate's boundary constraint.
Predicate<? super String> predicate1 = (CharSequence c)
-> c.toString().length() > 2 ;
System.out.println(predicate1.test("foo")); // compiles
Predicate<? super String> predicate2 = (CharSequence c)
-> c.toString().length() > 2 ;
System.out.println(predicate2.test((CharSequence)"foo")); // capture error
error: method test in interface Predicate<T> cannot be applied to given types;
out.println(predicate2.test((CharSequence)"foo"));
^
required: CAP#1
found: CharSequence
reason: argument mismatch; CharSequence cannot be converted to CAP#1
where T is a type-variable:
T extends Object declared in interface Predicate
where CAP#1 is a fresh type-variable:
CAP#1 extends Object super: String from capture of ? super String
Thanks for the work on this. The issue appears to be an assumption that the lambda and the generic process would forced to consume a CharSequence. However, it's now clear that String can be submitted to the lambda without a compiler error, so what's happening in the first case and a String is being submitted to both processes. It's no surprise that the generics process is ignoring the content of the lambda.
Predicate<? super String> predicate2 = (CharSequence c)
-> c.toString().length() > 2 ;
System.out.println(predicate2.test((CharSequence)"foo")); // capture error
The issue is that predicate2 says it is something which will accept a String; you're trying to pass it a CharSequence.
If you change the declaration of the variable to
Predicate<? super CharSequence> predicate2
then it works.
The reason this doesn't work is that you might be passing a MyCharSequence implements CharSequence to the predicate:
System.out.println(predicate2.test((CharSequence)new MyCharSequence()));
This shouldn't be accepted, because the type of the predicate2 variable says it should only expect to be given an instance of String. The compiler only sees the type of the actual parameter as CharSequence, so it can't distinguish between "this is a String" (would be OK) and "this is a MyCharSequence" (wouldn't be OK).
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