Основная тема Java 8 — лямбды. Я заметил, что для многих программистов на Java лямбды — довольно жесткий материал. Итак, давайте попробуем получить общее представление о них.
Прежде всего, что такое лямбда? Лямбда — это анонимная функция, которая, в отличие от обычной функции, не связана с идентификатором (то есть не имеет имени). Эти функции могут быть переданы в качестве аргументов другим функциям (известным как функции высшего порядка).
 Предположим, что наше приложение должно записывать в кучу файлов из разных мест системы.  Мы не хотим обрабатывать проверенные исключения каждый раз [см. Исключения: проверено и не отмечено для получения дополнительной информации о проверенных исключениях].  Поэтому мы решили написать низкоуровневую функцию writeToFile которая открывает FileWriter и передает его функции, которая затем может безопасно записать в файл. 
Используя эту низкоуровневую функцию, мы напишем следующий код.
| 
 1 
2 
3 
4 
5 
6 
7 
 | 
writeToFile("todo.txt", new FileWriteFunction() {    @Override    public void apply(Writer file) throws IOException {        file.write("learn about lambdas\n");        file.write("learn stream API\n");    }}) | 
  Объект, который мы передаем в writeToFile является анонимной реализацией FileWriteFunction [анонимно, потому что мы не writeToFile его как класс].  Он имеет единственную функцию, позволяющую эффективно передавать анонимную функцию.  В мире Java их иногда называют обратными вызовами .  Возможно, вы использовали это, по крайней мере, несколько раз раньше, возможно, не обращая внимания. 
Этот анонимный объект по сути является лямбда-выражением. Но, очевидно, это не похоже на передачу функции. Синтаксис очень громоздкий. И это именно то, что меняется в Java 8.
Благодаря синтаксической поддержке лямбд в Java 8 код читается так, как будто мы передаем функцию. Используя лямбду Java 8, мы переписываем приведенный выше код следующим образом.
| 
 1 
2 
3 
4 
 | 
writeToFile("todo.txt", file -> {    file.write("learn about lambdas\n");    file.write("learn stream API\n");}) | 
Так-то лучше. Он подчеркивает код, который имеет значение и скрывает большую часть громоздких частей.
Часто лямбды используются взаимозаменяемо с замыканиями (то есть лексическими функциями). Хотя они обе являются анонимными функциями, определение замыкания состоит в том, что это функция, содержащая связанные переменные. Т.е. замыкание включает в себя таблицу ссылок, которая содержит ссылки на локальные переменные.
  Например, если мы принимаем data параметров, которые мы хотим записать в файл, мы используем замыкание. 
| 
 1 
2 
3 
 | 
void save(String data) {    writeToFile("file.db", file -> file.write(data) );} | 
В то время как анонимные внутренние классы ограничивают доступ к конечным переменным, замыкания предоставляют доступ к любой переменной. Тем не менее, переменная является окончательной для закрытия, поэтому ее нельзя переназначить.
Как насчет составления лямбд? Предоставляет ли Java 8 только ложку синтаксических сахаров для анонимных внутренних классов только одним методом?
  Не на самом деле нет.  Это правда, что он допускает лямбда-синтаксис для любого анонимного внутреннего метода с одним методом.  Но лямбды не скомпилированы во внутренние классы.  Вместо этого компилятор выводит lambda$ методы в определяющем классе и использует invokedynamic для отправки вызова. 
Итак, теперь вы знаете, как использовать лямбду в Java 8. Хотя лямбды сами по себе довольно полезны, они особенно полезны при применении их к коллекциям.
  Новый Stream API предоставляет альтернативу итераторам, предлагая более функциональный API для коллекций: java.util.stream.Stream .  Наиболее примечательные функции Stream : collect , filter , map и reduce . 
Чтобы начать с простого примера, вот как суммировать все числа в списке.
| 
 1 
2 
 | 
asList(1,2,3,4,5).stream()    .reduce(0, (acc, value) -> acc + value) // => 15 | 
Это уменьшает последовательность, добавляя каждое значение к аккумулятору, начиная с нуля. Для сравнения классически вы бы написали цикл.
| 
 1 
2 
3 
4 
 | 
int acc = 0;for (int n : asList(1,2,3,4,5))    acc += n;acc // => 15 | 
  Переходим к суммированию только нечетных чисел.  Сначала мы filter нечетные числа, затем reduce . 
| 
 1 
2 
3 
 | 
asList(1,2,3,4,5).stream()    .filter(Predicates::odd)    .reduce(0, (acc, n) -> acc + n) // => 9 | 
  Аргумент для filter — это ссылка на функцию к odd статической функции в классе Predicates я использовал.  Это логическая функция, которая, как следует из названия, проверяет, является ли число нечетным. 
  Все идет нормально.  Теперь предположим, что мы хотим преобразовать список размеров сантиметров в их эквивалентный размер в дюймах.  Мы используем map для этого. 
| 
 1 
2 
3 
 | 
List<Inch> inches = centimeters.stream()    .map(Centimeter::toInches)    .collect(Collectors.toList()) | 
  toInches отображаются в дюймы с помощью функции toInches для каждого элемента в коллекции centimeters . 
  По своей природе Stream непрерывен.  Он используется для описания операций, применяемых к коллекции.  Но для получения результатов данные должны быть собраны.  Для этого предназначена функция collect .  Это уменьшает элементы потока в изменяемый контейнер [например, список]. 
  Использование Stream API и lambdas может значительно упростить код, который вы используете для работы с коллекциями, и сделать код намного более выразительным.  Предпочтение использования неразрушающих операций (например, map , filter ) по сравнению с разрушительными операциями (например, forEach ) делает код более легким для рассуждения. 
Это оно! Это основы, которые вы должны знать о лямбдах (и замыканиях) в Java 8. Конечно, о лямбдах можно написать гораздо больше, но это кое-что для другого поста.