Возвращаемое значение лямбда в Java

до сих пор мне удается найти все ответы, которые мне нужны, но это меня смущает. Допустим, у нас есть пример кода:

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());
   }
}

учебное пособие OCA (экзамен 1Z0-808) книга говорит, что эти две строки эквивалентны:

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

означает ли это, что за кулисами Java добавляет ключевое слово возвращение кодировать в первом случае?

если ответ да, то как следующий код компилируется (представьте, что все остальное в порядке место):

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

если мы знаем, что подписи выполнить и выполнимых по run являются:

void execute(Runnable command)
void run()

если ответ нет, то как Java знает, когда ему нужно что-то вернуть, а когда нет? Может быть, в

a -> a.canHop()

случай, который мы хотели игнорировать boolean возвращаемый тип метода.

3 ответов


означает ли это, что за кулисами Java добавляет ключевое слово return в код в первом случае?

нет, компилятор генерирует байтовый код, и он может генерировать тот же байтовый код, но он не изменяет синтаксис, а затем компилирует его снова.

мы хотели игнорировать логический тип возвращаемого метода.

он имеет возможность игнорировать значение, основанное на том, какие функциональные интерфейсы это принимая во внимание.

a -> a.canHop()

может быть

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

или

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

на основе контекста, однако он благоприятствует первому, если это возможно.

считают ExecutorService.submit(Callable<T>) и ExecutorService.submit(Runnable)

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

сохранение типа возврата вы можете видеть, что это Callable<Integer>

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

чтобы попробовать себя, вот более длинный пример.

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();
}

печать

null
2
counter: 3
null

первый submit взять Runnable так Future.get() возвращает null

второй submit по умолчанию являясь Callable так Future.get() возвращает 2

третий submit может быть только void возвращаемое значение, поэтому оно должно быть Runnable так Future.get() возвращает null


да, при указании только одного оператора, то его значение автоматически возвращается от лямбды.

С Runnable функциональный интерфейс, его можно определить как лямбда. Тип возврата -void, поэтому любое возвращаемое значение внутри лямбды будет проигнорировано.


вы запутались в рамки return заявление. The (вставляется ли как байт-код компилятором или как исходный код программистом) возвращается из лямбда-кода, а не из метода, вызывающего лямбда-код.

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");
}