Имеют ли лямбда-выражения какое-либо использование, кроме сохранения строк кода?

Используются ли лямбда-выражения, кроме сохранения строк кода?

Есть ли какие-либо специальные функции, предоставляемые лямбдами, которые решают проблемы, которые нелегко решить? Типичное использование, которое я видел, заключается в том, что вместо написания этого:

Comparator<Developer> byName = new Comparator<Developer>() {
  @Override
  public int compare(Developer o1, Developer o2) {
    return o1.getName().compareTo(o2.getName());
  }
};

Мы можем использовать лямбда-выражение для сокращения кода:

Comparator<Developer> byName =
(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName());

Ответ 1

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

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

В частности, лямбда-выражения определяются независимо от функционального интерфейса, к которому они будут преобразованы, поэтому нет наследованных членов, к которым они могли бы получить доступ, кроме того, они не могут получить доступ к экземпляру типа, реализующего функциональный интерфейс. В пределах лямбда-выражения this и super имеют то же значение, что и в окружающем контексте, см. Также этот ответ. Кроме того, вы не можете создавать новые локальные переменные, затеняя локальные переменные окружающего контекста. Для предполагаемой задачи определения функции это устраняет множество источников ошибок, но также подразумевает, что для других случаев использования могут быть анонимные внутренние классы, которые не могут быть преобразованы в выражение лямбда, даже если они реализуют функциональный интерфейс.

Кроме того, конструкция new Type() { … } гарантирует создание нового отдельного экземпляра (поскольку new всегда делает). Анонимные внутренние экземпляры классов всегда содержат ссылку на свой внешний экземпляр, если они созданы в контексте static. Напротив, лямбда-выражения только фиксируют ссылку на this, когда это необходимо, то есть если они обращаются к элементу this или не static. И они создают экземпляры намеренно неопределенной личности, что позволяет реализации во время выполнения решать, следует ли повторно использовать существующие экземпляры (см. Также "Является ли лямбда-выражение созданием объекта в куче каждый раз, когда оно казнят?" ).

Эти различия относятся к вашему примеру. Ваша анонимная внутренняя конструкция класса всегда будет создавать новый экземпляр, также он может захватить ссылку на внешний экземпляр, тогда как ваш (Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName()) является не захватывающим лямбда-выражением, которое будет оцениваться в одноэлементном режиме в типичных реализациях. Кроме того, он не создает файл .class на вашем жестком диске.

Учитывая различия в семантике и производительности, лямбда-выражения могут изменить то, как программисты решат определенные проблемы в будущем, конечно, также из-за новых API, охватывающих идеи функционального программирования с использованием новых языковых функций. См. Также Ява-выражение Java 8 и значения первого класса.

Ответ 2

Языки программирования не предназначены для машин.

Они предназначены для программистов.

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

Я не буду взвешивать, хорошо это или плохо: все это компромиссы. Но Java 8 lambdas позволяют программистам мыслить с точки зрения функций, которые вы ранее не могли сделать на Java.

Это то же самое, что и процедурный программист, который учится мыслить в терминах классов, когда они приходят на Java: вы видите, что они постепенно переходят из классов, которые являются прославленными структурами, и имеют "вспомогательные" классы с кучей статических методов и перемещаются к чему-то, что более близко напоминает рациональный дизайн OO (mea culpa).

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

Ответ 3

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

Без лямбда-выражений (и/или ссылок на методы) Stream конвейеры были бы гораздо менее удобочитаемыми.

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

List<String> names =
    people.stream()
          .filter(p -> p.getAge() > 21)
          .map(p -> p.getName())
          .sorted((n1,n2) -> n1.compareToIgnoreCase(n2))
          .collect(Collectors.toList());

Это будет:

List<String> names =
    people.stream()
          .filter(new Predicate<Person>() {
              @Override
              public boolean test(Person p) {
                  return p.getAge() > 21;
              }
          })
          .map(new Function<Person,String>() {
              @Override
              public String apply(Person p) {
                  return p.getName();
              }
          })
          .sorted(new Comparator<String>() {
              @Override
              public int compare(String n1, String n2) {
                  return n1.compareToIgnoreCase(n2);
              }
          })
          .collect(Collectors.toList());

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

И это относительно короткий конвейер.

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

Ответ 4

Внутренняя итерация

При повторении Java-коллекций большинство разработчиков стремятся получить элемент, а затем процесс. Это, взять этот элемент, а затем использовать его или повторно вставить его и т.д. В версиях до 8 версий Java вы можете реализовать внутренний класс и сделать что-то вроде:

numbers.forEach(new Consumer<Integer>() {
    public void accept(Integer value) {
        System.out.println(value);
    }
});

Теперь с Java 8 вы можете сделать лучше и менее подробно:

numbers.forEach((Integer value) -> System.out.println(value));

или лучше

numbers.forEach(System.out::println);

Поведение в качестве аргументов

Угадайте следующий случай:

public int sumAllEven(List<Integer> numbers) {
    int total = 0;

    for (int number : numbers) {
        if (number % 2 == 0) {
            total += number;
        }
    } 
    return total;
}

С Java 8 Интерфейс предикатов вы можете сделать так:

public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
    int total = 0;

    for (int number : numbers) {
        if (p.test(number)) {
            total += number;
        }
    }
    return total;
}

Вызов:

sumAll(numbers, n -> n % 2 == 0);

Источник: DZone - зачем нам нужны выражения лямбда в Java

Ответ 5

Существует много преимуществ использования lambdas вместо внутреннего класса, как показано ниже:

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

  • Используя lambdas, вы с удовольствием программируете функциональные операции над потоками элементов, такие как преобразования map-reduce в коллекциях. см. java.util.function и java.util.stream пакеты.

  • Нет файла физических классов, сгенерированного для lambdas компилятором. Таким образом, ваши приложения становятся меньше. Как память назначается лямбда?

  • Компилятор оптимизирует создание лямбда, если лямбда не имеет доступа к переменным из своей области, что означает, что экземпляр лямбды создается только один раз JVM. для более подробной информации вы можете увидеть ответ @Holger вопроса Является ли ссылка на метод ссылкой хорошей идеей в Java 8?.

  • Lambdas может реализовывать интерфейсы с несколькими маркерами помимо функционального интерфейса, но анонимные внутренние классы не могут реализовывать больше интерфейсов, например:

    //                 v--- create the lambda locally.
    Consumer<Integer> action = (Consumer<Integer> & Serializable) it -> {/*TODO*/};
    

Ответ 6

Чтобы ответить на ваш вопрос, дело в том, что lambdas не позволяет делать что-либо, что вы не могли сделать до java-8, скорее, это позволяет вам писать более сжатый код. Преимущества этого в том, что ваш код будет более четким и гибким.

Ответ 7

Одна вещь, о которой я еще не упоминал, заключается в том, что lambda позволяет вам определять функциональность, в которой она использовалась.

Итак, если у вас есть простая функция выбора, вам не нужно помещать ее в отдельное место с кучей шаблона, вы просто пишете лямбду, которая будет краткими и локальными.

Ответ 8

Есть много преимуществ.

  • Не нужно определять весь класс, мы можем передать реализацию функции в качестве ссылки.
    • Внутреннее создание класса создаст файл .class, а если вы используете lambda, тогда создание класса будет исключено компилятором, потому что в lambda вы передаете реализацию функции вместо класса.
  • Повторное использование кода выше, чем до
  • И как вы сказали, код короче, чем нормальная реализация.

Ответ 9

Lambdas - это просто синтаксический сахар для анонимных классов.

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

Если вы используете IntelliJ IDEA, он может выполнить преобразование для вас:

  • Поместите курсор в лямбда
  • Нажмите alt/option + enter

введите описание изображения здесь