Статьи

Java 8: преобразование анонимных классов в лямбда-выражения

Рефакторинг анонимных классов (которые реализуют один единственный метод) в лямбда-выражения делает ваш код более лаконичным и читабельным. Например, вот анонимный класс для Runnable и его лямбда-эквивалент:

01
02
03
04
05
06
07
08
09
10
// using an anonymous class
Runnable r = new Runnable() {
  @Override
  public void run() {
    System.out.println("Hello");
  }
};
 
// using a lambda expression
Runnable r2 = () -> System.out.println("Hello");

Однако это не всегда так просто!

Вот несколько ошибок:

1. Различные правила определения объема

Существуют разные правила определения объема между анонимными классами и лямбда-выражениями. Например, в лямбда-выражениях this и super имеют лексическую область видимости, то есть они относятся к включающему классу, но в анонимном классе они относятся к самому анонимному классу. Точно так же локальные переменные, объявленные в лямбда-выражениях, будут конфликтовать с переменными, объявленными в классе включения, но в анонимных классах им разрешено скрывать переменные в классе включения. Вот пример:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
int foo = 1;
Runnable r = new Runnable() {
  @Override
  public void run() {
    // this is ok!
    int foo = 2;
  }
};
 
Runnable r2 = () -> {
  // compile error: Lambda expression's local variable foo cannot
  // redeclare another local variable defined in an enclosing scope.
  int foo = 2;
};

2. Перегруженные методы

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Functional interface
interface Task {
  public void execute();
}
 
// Overloaded methods
public static void go(final Runnable r) {
  r.run();
}
public static void go(final Task t) {
  t.execute();
}
 
// Calling the overloaded method:
 
// When using an anonymous class, there is no ambiguity because
// the type of the class is explicit at instantiation
go(new Task() {
  @Override
  public void execute() {
     System.out.println("Hello");
  }
});
 
// When using a lambda expression, there is a compile error!
// The method go(Runnable) is ambiguous
go(() -> {
  System.out.println("Hello");
});
 
// This ambiguity can be solved with an explicit cast
go((Task)() -> {
  System.out.println("Hello");
});