Le code Java suivant ne parvient pas à se compiler:
@FunctionalInterface
private interface BiConsumer<A, B> {
void accept(A a, B b);
}
private static void takeBiConsumer(BiConsumer<String, String> bc) { }
public static void main(String[] args) {
takeBiConsumer((String s1, String s2) -> new String("hi")); // OK
takeBiConsumer((String s1, String s2) -> "hi"); // Error
}
Le compilateur rapporte:
Error:(31, 58) java: incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
La chose étrange est que la ligne marquée "OK" se compile correctement, mais la ligne marquée "Erreur" échoue. Ils semblent essentiellement identiques.
{ }
detakeBiConsumer
... et si oui, pourriez-vous donner un exemple ... si je lis ceci correctement,bc
est une instance de la classe / interfaceBiConsumer
, et devrait donc contenir une méthode appeléeaccept
pour correspondre à la signature de l'interface. .. ... et si c'est vrai, alors laaccept
méthode doit être définie quelque part (par exemple, une classe qui implémente l'interface) ... c'est donc ce qui devrait être dans le{}
?? ... ... ... merci(String s1, String s2) -> "hi"
est une instance de BiConsumer <String, String>.Réponses:
Votre lambda doit être en accord avec
BiConsumer<String, String>
. Si vous vous référez au JLS # 15.27.3 (Type of a Lambda) :Ainsi, le lambda doit être une expression d'instruction ou un bloc compatible void:
la source
Fondamentalement,
new String("hi")
est un morceau de code exécutable qui fait réellement quelque chose (il crée une nouvelle chaîne et la renvoie). La valeur retournée peut être ignorée etnew String("hi")
peut toujours être utilisée dans void-return lambda pour créer une nouvelle chaîne.Cependant,
"hi"
c'est juste une constante qui ne fait rien par elle-même. La seule chose raisonnable à faire avec un corps lambda est de le retourner . Mais la méthode lambda devrait avoir le type de retourString
ouObject
, mais elle retournevoid
, d'où l'String cannot be casted to void
erreur.la source
String
littéral est simplement une expression qui ne peut pas être utilisée dans un contexte d' instruction .()->x++
est légal, alors que()->(x++)
, fondamentalement, faire exactement la même chose, n'est pas…Le premier cas est correct car vous invoquez une méthode "spéciale" (un constructeur) et vous ne prenez pas réellement l'objet créé. Juste pour être plus clair, je vais mettre les accolades optionnelles dans vos lambdas:
takeBiConsumer((String s1, String s2) -> {new String("hi");}); // OK takeBiConsumer((String s1, String s2) -> {"hi"}); // Error
Et plus clair, je vais traduire cela dans l'ancienne notation:
takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) { public void accept(String s, String s2) { new String("hi"); // OK } }); takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) { public void accept(String s, String s2) { "hi"; // Here, the compiler will attempt to add a "return" // keyword before the "hi", but then it will fail // with "compiler error ... bla bla ... // java.lang.String cannot be converted to void" } });
Dans le premier cas, vous exécutez un constructeur, mais vous ne retournez PAS l'objet créé, dans le second cas, vous essayez de renvoyer une valeur String, mais votre méthode dans votre interface
BiConsumer
renvoie void, d'où l'erreur du compilateur.la source
Le JLS précise que
Voyons maintenant cela en détail,
Puisque votre
takeBiConsumer
méthode est de type void, la réception lambdanew String("hi")
l'interprétera comme un bloc comme{ new String("hi"); }
qui est valide dans le vide, d'où la première compilation de cas.
Cependant, dans le cas où le lambda est
-> "hi"
, un bloc tel que{ "hi"; }
n'est pas une syntaxe valide en java. Par conséquent, la seule chose à faire avec "hi" est d'essayer de le renvoyer.
{ return "hi"; }
qui n'est pas valide dans le vide et expliquer le message d'erreur
incompatible types: bad return type in lambda expression java.lang.String cannot be converted to void
Pour une meilleure compréhension, notez que si vous changez le type de
takeBiConsumer
en String,-> "hi"
sera valide car il essaiera simplement de renvoyer directement la chaîne.Notez qu'au début, j'ai pensé que l'erreur était due au fait que le lambda était dans un mauvais contexte d'invocation, je vais donc partager cette possibilité avec la communauté:
JLS 15.27
Cependant dans notre cas, nous sommes dans un contexte d'invocation qui est correct.
la source