J'ai du mal à comprendre le code suivant sur les deux Predicate
objets. La première utilise un caractère générique borné inférieure, la seconde une partie supérieure délimitée.
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?)
Ce que je ne comprends pas p1
, pourquoi est - il possible d'appeler des méthodes de la classe String
, par exemple startsWith()
? Pourquoi ne puis - je passer que des String
objets dans p1.test()
, je pensais pouvoir l' appeler pour Number
et des Object
objets ainsi.
Comme p1
se comporte , je pensais p2
Plût, mais ce n'est pas le cas. Je ne peux pas passer même un String
objet dans p2.test()
. Cela ne me paraît logique, parce que nous nous attendons à un objet qui hérite de String
(y compris String
).
Je pense qu'il a peut-être quelque chose à voir avec le fait que nous précisons le type de référence plutôt que le type de l'objet lui-même. Mais quel type est alors utilisé pour l'objet?
Il est légal pour vous d'appeler startsWith
pour p1
, même si p1
est tapé avec une limite inférieure ? super String
, parce que l'argument de type est déduit être String
. L'expression lambda s -> s.startsWith("a");
est déduit d'être Predicate<String>
, ce qui est légal d'assigner à une variable de type Predicate<? super String>
.
Cette compiles:
Predicate<String> ps = s -> s.startsWith("a");
Predicate<? super String> p1 = ps;
Cela ne signifie pas:
// no "startsWith" on Object
Predicate<? super String> p1 = (Object s) -> s.startsWith("a");
La référence de JLS est dans la section 15.27.3 , "type d'une expression lambda".
Si T est un type d'interface fonctionnelle paramétrés générique et l'expression lambda est typée implicitement, le type de cible au sol est la paramétrisation non générique (§9.9) de T.
Ici, le type de cible au sol est le type de l'expression lambda, et T
est le type de cible, qui est ici votre type de données variable limite inférieure. Cela permet au compilateur d'assigner Predicate<String>
comme type de cible au sol, qui devient le type de l'expression lambda.
Notez également que vous ne pouvez pas passer un objet à superclasse p1.test
, parce que vous pouvez (et vous avez déjà) attribué un Predicate<String>
à p1
qui prend String
.
p1.test(new Object()); // Error: can't pass something higher than String
Quant à savoir pourquoi vous ne pouvez pas passer un String
à p2.test
, lorsque vous avez un caractère générique supérieur bornés tels que ? extends String
, cela signifie que le paramètre de type peut être une classe qui est soit String
ou un sous - type. (Le compilateur ignore qui String
est - final
là et il ne peut y avoir de sous - classes String
.) Le prédicat p2
pourrait se voir attribuer une Predicate<SillyString>
, en présumant SillyString
est une sous - classe String
. Mais vous ne pouvez pas passer une String
à une méthode qui pourrait s'attendre à un SillyString
.