Java中使用lambda表达式返回值

15

到目前为止,我已经成功找到了所有我所需要的答案,但这一个让我感到困惑。假设我们有以下示例代码:

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会在第一种情况下将关键字return添加到代码中?

如果答案是YES,则下一个代码如何编译(假设其他所有内容都正确):

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

如果我们知道execute和Runnable的run的签名是:

void execute(Runnable command)
void run()

如果答案是否定的,那么Java如何知道什么时候需要返回值,什么时候不需要呢?也许在

a -> a.canHop()

如果我们希望忽略方法的 布尔 返回类型。


2
它知道在Runnable情况下可以忽略返回类型,因为run()返回void。而且它知道在CheckAnimal情况下不能忽略返回类型,因为test()不返回void。 - JB Nizet
1
Lambda做的是方法应该做的事情,如果你的方法包含返回类型,那么lambda将提供,否则不提供。它是编写方法的快捷方式,所以不要让自己感到困惑。 - bananas
3个回答

15

这是否意味着,在幕后,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时:es.execute(() -> counter++); 那么这是否被视为副作用呢? - Freeman
@Freeman,任何不返回值的方法只能具有副作用。 - Peter Lawrey

6
您对`return`语句的范围感到困惑。无论是编译器插入的字节码还是程序员编写的源代码,`return`语句都会从lambda中返回,而不是从调用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");
}

5

是的,当只指定单个语句时,lambda 自动返回它的值。

Runnable 是一个函数式接口,因此可以定义为 lambda 表达式。返回类型为 void,所以在 lambda 中的任何返回值都将被忽略。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接