web-dev-qa-db-fra.com

Renvoie la valeur par lambda dans Java

Jusqu'à présent, je parviens à trouver toutes les réponses dont j'ai besoin, mais celle-ci me déroute. Disons que nous avons un exemple de code:

public class Animal {
   private String species;
   private boolean canHop;
   private boolean canSwim;
   public Animal(String speciesName, boolean hopper, boolean swimmer) {
     species = speciesName;
     canHop = hopper;
     canSwim = swimmer;
   }
  public boolean canHop() { return canHop; }
  public boolean canSwim() { return canSwim; }
  public String toString() { return species; }
}

public interface CheckAnimal {
   public boolean test(Animal a);
}

public class FindSameAnimals {
   private static void print(Animal animal, CheckAnimal trait) {
      if(trait.test(animal)){
         System.out.println(animal);
      }

   public static void main(String[] args) {
      print(new Animal("fish", false, true), a -> a.canHop());
   }
}

Le livre OCA Study Guide (Exam 1Z0-808) dit que ces deux lignes sont équivalentes:

a -> a.canHop()
(Animal a) -> { return a.canHop(); }

Cela signifie-t-il que, en arrière-plan, Java ajoute le mot clé return to code in le premier cas?

Si la réponse est OUI, alors comment compiler le code suivant (imaginez que tout le reste est au bon endroit):

static int counter = 0;
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(() -> counter++));

si nous savons que les signatures pour exécutent et Runnable run sont:

void execute(Runnable command)
void run()

Si la réponse est NON, alors comment Java savoir quand il doit retourner quelque chose et quand ne pas le faire?

a -> a.canHop()

cas où nous voulions ignorer booléen retourne le type de méthode.

9
Freeman

Est-ce à dire que, dans les coulisses, Java ajoute le mot-clé return au code dans le premier cas?

Non, le compilateur génère du code octet, et il peut générer le même code octet, mais il ne modifie pas la syntaxe, puis le recompile.

nous voulions ignorer le type de méthode de retour booléen.

Il a la possibilité d'ignorer une valeur basée sur les interfaces fonctionnelles qu'il envisage.

a -> a.canHop()

pourrait être

(Animal a) -> { return a.canHop(); }

ou

(Animal a) -> { a.canHop(); }

en fonction du contexte, mais il privilégie le premier si possible.

Considérez ExecutorService.submit(Callable<T>) et ExecutorService.submit(Runnable)

ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(() -> counter++); // has to be Runnable
es.submit(() -> counter++); // Callable<Integer> or Runnable?

En enregistrant le type de retour, vous pouvez voir que c'est un Callable<Integer>

final Future<Integer> submit = es.submit(() -> counter++);

Pour vous essayer, voici un exemple plus long.

static int counter = 0;

public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService es = Executors.newSingleThreadExecutor();

    // execute only takes Runnable
    es.execute(() -> counter++);

    // force the lambda to be Runnable
    final Future<?> submit = es.submit((Runnable) () -> counter++);
    System.out.println(submit.get());

    // returns a value so it's a Callable<Integer>
    final Future<Integer> submit2 = es.submit(() -> counter++);
    System.out.println(submit2.get());

    // returns nothing so it must be Runnable
    final Future<?> submit3 = es.submit(() -> System.out.println("counter: " + counter));
    System.out.println(submit3.get());

    es.shutdown();
}

impressions

null
2
counter: 3
null

Le premier submit prend un Runnable donc Future.get() renvoie null

Le second submit par défaut est un Callable donc Future.get() renvoie 2

La troisième submit ne peut être qu'une valeur de retour void, elle doit donc être une Runnable donc Future.get() renvoie null

11
Peter Lawrey

Oui, lorsque vous ne spécifiez qu'une seule instruction, sa valeur est automatiquement renvoyée par le lambda.

Ensuite, puisque Runnable est une interface fonctionnelle, elle peut être définie comme un lambda. Le type de retour est void, donc toute valeur de retour à l'intérieur du lambda sera ignorée.

3
Guillaume Poussel

Vous êtes confus quant à la portée de l'instruction return. L'instruction return (qu'elle soit insérée en tant que bytecode par le compilateur ou en tant que code source par le programmeur) renvoie du lambda, et non de la méthode qui appelle le lambda.

void foo() {
    Supplier<String> s = () -> { return "bar" };
    String s = s.get(); // s is assigned to "bar"
    // Execution continues as the return statement in the lambda only returns from the lambda and not the enclosing method
    System.out.println("This will print");
}
2
SamTebbs33