Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a cast performed against a lambda input parameter?

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.

like image 923
John Avatar asked Jun 24 '20 19:06

John


1 Answers

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).

like image 163
Andy Turner Avatar answered Oct 19 '22 22:10

Andy Turner