Для вас законно вызывать startsWith
для p1
, хотя p1
набирается с нижней границей ? super String
, потому что аргумент типа выводится как String
. Лямбда-выражение 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 находится в Раздел 15.27.3 , "Тип лямбда-выражения".
Если T является параметризованным подстановочным знакомтип функционального интерфейса и лямбда-выражение вводятся неявно, тогда тип наземной цели - это параметризация без подстановочных знаков (§9.9) T.
Здесь тип наземной цели является типом лямбда-выражения, а T
является целевым типом, который здесь представляет собой тип переменной вашей нижней границы. Это позволяет компилятору назначить Predicate<String>
в качестве основного целевого типа, который становится типом лямбда-выражения.
Также обратите внимание, что вы не можете передать объект суперкласса в p1.test
, потому что вы могли (и вы уже назначили Predicate<String>
на p1
, что занимает String
.
p1.test(new Object()); // Error: can't pass something higher than String
Что касается того, почему вы не можете передать String
на p2.test
, когда выиметь ограниченный сверху символ подстановки, такой как ? extends String
, это означает, что параметром типа может быть любой класс, который является String
или подтипом. (Компилятор игнорирует, что String
здесь final
, и не может быть никаких подклассов String
.) Предикату p2
можно присвоить Predicate<SillyString>
, предполагая, что SillyString
является подклассом String
. Но вы не можете передать String
методу, который может ожидать SillyString
.