В чём разница разница между лямбда-выражениями и ссылками на методы?

184
13 декабря 2018, 00:50

IntelliJ IDEA предлагает заменить лямбда-выражения ссылками на методы. В чём разница разница между ними?

Answer 1

Принципиальная разница вот в чём: лямбда - это всегда новый метод в классе. Если декомпилировать вот такой код:

Function<String, String> f = s -> s.toUpperCase();
f.apply("abc");

получим такой байткод:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      // байткод main
  private static java.lang.String lambda$main$0(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
      // байткод лямбды

Для сравнения, код со ссылкой на метод:

Function<String, String> f = String::toUpperCase;
f.apply("abc");

даст такой байткод:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      // байткод main

Каждая лямбда в байткоде превращается в приватный синтетический метод с именем вроде lambda$0, который будет вызываться через invokedynamic каждый раз, когда нужна лямбда. Поэтому IDEA и советует вам заменить лямбду на ссылку на метод - так байткод класса будет компактнее, а значит, класс быстрее загрузится в JVM. Само собой, для того чтобы ощутить такой прирост, нужны тысячи неоптимизированных лямбд, но в крупном проекте такое количество наберётся легко.

В остальном выбор между лямбдой и ссылкой на метод - вопрос вкуса и читаемости кода. Например, если в лямбде несколько строк, читаемость снижается, и код только выиграет, если лямбду вынести в отдельный метод, и использовать ссылку на него. Или, например, если есть класс ClassWithVeryVeryVeryLongName с методом doSth(), то лямбда c -> c.doSth() будет читаться проще, чем ClassWithVeryVeryVeryLongName::doSth.

Answer 2

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

Об этом более или менее подробно описано в примере:

Arrays.sort(rosterAsArray,
     (a, b) -> Person.compareByAge(a, b)
);

Поскольку это лямбда-выражение вызывает существующий метод, вы можете использовать ссылку на метод вместо выражения лямбда:

Arrays.sort(rosterAsArray, Person::compareByAge);

Ссылка метода Person::compareByAge семантически совпадает с выражением лямбда (a, b) -> Person.compareByAge(a, b). Каждый из них имеет следующие характеристики:

  • Его формальный список параметров копируется из Comparator<Person>.compare, который является (Person, Person).
  • Его тело вызывает метод Person.compareByAge.
READ ALSO
Не создается таблица даных Hibernate MySql

Не создается таблица даных Hibernate MySql

Перепробовал разные методыОшибок подключения к базе вроде нет

134
Exoplayer повтор видео с общим TimeBar

Exoplayer повтор видео с общим TimeBar

Возможно ли соединить два(или больше) видео так, что бы они имели общий TimeBar(длина была в длину обеих видио, и пользователь мог менять позицию)В...

172
Парсинг таймзоны &#39;-030-6&#39;

Парсинг таймзоны '-030-6'

С js-клиента прилетает дата в виде: 1909-09-09T14:27:45000-030-6

162
Exception in thread &ldquo;JavaFX Application Thread&rdquo;

Exception in thread “JavaFX Application Thread”

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

173