Java 8 была выпущена 4 года назад, 18 марта 2014 г., и эта революционная версия вызвала бурные обсуждения и волнения в сообществе Java. Одной из особенностей является сопровождающее лямбда-выражение, которое позволит нам передать поведение в функцию. До Java 8, если вы хотели передать поведение в функцию, единственным вариантом был анонимный класс, который требовал 6 строк кода. Строка кода, определяющая наиболее важное поведение, недостаточно заметна посередине. Лямбда-выражения заменяют анонимные классы, избавляются от шаблонов и позволяют писать код в функциональном стиле. Это иногда более читабельно и выражается яснее.
В экосистеме Java функциональные выражения и полная поддержка объектной ориентации являются впечатляющими достижениями. Это будет способствовать дальнейшему развитию параллельных сторонних библиотек, позволяющих в полной мере использовать преимущества многоядерных процессоров.
Хотя индустрии потребуется время, чтобы переварить Java 8, я не думаю, что какой-либо серьезный разработчик Java должен игнорировать основные функции этой версии Java, а именно лямбда-выражения, функциональные интерфейсы, потоковые API, методы по умолчанию и новые дату и время. API.
Как разработчик, я обнаружил, что лучший способ изучить и освоить лямбда-выражения — это проявить авантюризм и попрактиковаться на как можно большем количестве примеров лямбда-выражений. Учитывая, что среда коллекций Java больше всего пострадала от выпуска Java 8, рекомендуется попрактиковаться с потоковыми API и лямбда-выражениями для извлечения, фильтрации и сортировки данных списков и коллекций.
Я писал о Java 8 и в прошлом делился некоторыми ресурсами, чтобы помочь вам всем.Мастер Java 8. В этой статье рассказывается об использовании 10 самых полезных лямбда-выражений в коде. Эти короткие и лаконичные примеры помогут вам быстро изучить лямбда-выражения.
Пример лямбда-выражения Java 8
Я лично очень рад выпуску Java 8, особенно лямбда-выражениям и потоковому API. Чем больше я о них знаю, тем чище я могу писать. Хотя сначала такого не было. Когда я впервые увидел код Java, написанный с помощью лямбда-выражений, я был очень разочарован загадочным синтаксисом, думая, что они делают Java нечитаемым, но я ошибался. Проведя день, практикуясь с некоторыми лямбда-выражениями и примерами потокового API, я был рад увидеть более чистый код Java. Это немного похоже на обучениеДженерики, я ненавидел это в первый раз, когда я видел это. Я даже продолжал использовать старую Java 1.4 для коллекций, пока однажды друг не познакомил меня с преимуществами использования дженериков (и не понял этого). Итак, основная позиция такова: не бойтесь загадочного синтаксиса лямбда-выражений и ссылок на методы, после выполнения нескольких упражнений, извлечения и фильтрации данных из классов коллекций вам это понравится. Давайте начнем изучение лямбда-выражений Java 8, начав с простого примера.
Пример 1. Реализация Runnable с лямбда-выражениями
Когда я начал использовать Java 8, первое, что я сделал, это заменил анонимные классы лямбда-выражениями при реализацииRunnableИнтерфейс — лучший пример анонимного класса. Взгляните на пред-Java 8runnableДля реализации метода требуется 4 строки кода, а для использования лямбда-выражения требуется только одна строка кода. Что мы здесь делаем? То есть заменить весь блок кода на () -> {}анонимный класс.
// Java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8, too much code for too little to do");
}
}).start();
//Java 8方式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
вывод:
too much code, for too little to do
Lambda expression rocks !!
Этот пример показывает нам синтаксис лямбда-выражений Java 8. Вы можете использовать lambda для написания такого кода:
(params) -> expression
(params) -> statement
(params) -> { statements }
Например, если ваш метод не изменяет и не переопределяет параметры, а просто выводит что-то на консоль, вы можете написать:
() -> System.out.println("Hello Lambda Expressions");
Если ваш метод принимает два параметра, его можно записать следующим образом:
(int even, int odd) -> even + odd
Кстати, принято делать имена переменных внутри лямбда-выражений короче. Это делает код короче и в той же строке. Поэтому в приведенном выше коде лучше выбрать a, b или x, y для имени переменной, чем четное и нечетное.
Пример 2. Обработка событий с использованием лямбда-выражений Java 8
Если вы программировали с помощью Swing API, вы помните, как писать код прослушивателя событий. Это снова классический вариант использования простого анонимного класса в старой версии, но теперь можно сделать иначе. Вы можете написать лучший код прослушивателя событий с помощью лямбда-выражений, например:
// Java 8之前:
JButton show = new JButton("Show");
show.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Event handling without lambda expression is boring");
}
});
// Java 8方式:
show.addActionListener((e) -> {
System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});
Другое место, где Java-разработчики часто используют анонимные классы, — этоCollections.sort()сделанный на заказComparator. В Java 8 вы можете заменить уродливые анонимные классы более читабельными лямбда-выражениями. Я оставлю это в качестве упражнения, это не должно быть сложно, это можно реализовать, поскольку я использую лямбда-выражения.RunnableиActionListenerрутина в процессе, чтобы сделать это.
Пример 3. Перебор списка с использованием лямбда-выражения
Если вы уже несколько лет работаете с Java, то знаете, что наиболее распространенной операцией с классами коллекций является повторение и применение бизнес-логики к отдельным элементам, таким как обработка списков заказов, транзакций и событий. Поскольку Java — императивный язык, весь циклический код до Java 8 был последовательным, т. е. его элементы можно было распараллелить. Если вы хотите выполнять параллельную фильтрацию, вам нужно написать код самостоятельно, что не так просто. Благодаря внедрению лямбда-выражений и методов по умолчанию вопрос о том, что и как делать, отделяется, а это значит, что коллекции Java теперь умеют выполнять итерации и могут распараллеливать элементы коллекции на уровне API. В следующем примере я покажу, какиспользоватьlambdaИли перебрать список без использования лямбда-выражения. Вы можете видеть, что в списке теперь есть метод forEach(), который перебирает все объекты и применяет к ним ваш лямбда-код.
// Java 8之前:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
System.out.println(feature);
}
// Java 8之后:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));
// 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示,
// 看起来像C++的作用域解析运算符
features.forEach(System.out::println);
вывод:
Lambdas
Default Method
Stream API
Date and Time API
цикл спискаВ последнем примере показано, как использовать ссылки на методы в Java 8. Вы можете видеть, что двойное двоеточие, оператор разрешения области видимости в C++, теперь используется в Java 8 для представления ссылок на методы.
Пример 4. Использование лямбда-выражений и функционального интерфейса Predicate
Помимо поддержки стиля функционального программирования на уровне языка, в Java 8 также добавлен пакет под названием java.util.function. Он содержит множество классов для поддержки функционального программирования на Java. Один из них являетсяPredicate,использоватьjava.util.function.PredicateФункциональные интерфейсы, а также лямбда-выражения могут добавлять логику к методам API для поддержки более динамичного поведения с меньшим количеством кода. Ниже приведен пример предиката Java 8, который показывает несколько распространенных способов фильтрации данных коллекции.PredicateИнтерфейсы отлично подходят для фильтрации.
public static void main(String[] args) {
List<String> languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
System.out.println("Languages which starts with J :");
filter(languages, (str)->((String)str).startsWith("J"));
System.out.println("Languages which ends with a ");
filter(languages, (str)->((String)str).endsWith("a"));
System.out.println("Print all languages :");
filter(languages, (str)->true);
System.out.println("Print no language : ");
filter(languages, (str)->false);
System.out.println("Print language whose length greater than 4:");
filter(languages, (str)->((String)str).length() > 4);
}
public static void filter(List<String> names, Predicate condition) {
for(String name: names) {
if(condition.test(name)) {
System.out.println(name + " ");
}
}
}
вывод:
Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell
// 更好的办法
public static void filter(List names, Predicate condition) {
names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
System.out.println(name + " ");
});
}
Как видите, метод filter Stream API также принимаетPredicate, что означает, что мы можем заменить наш пользовательский метод filter() встроенным кодом, написанным внутри, что является магией лямбда-выражений. Кроме того,PredicateИнтерфейс также позволяет тестировать несколько условий, как будет описано в следующем примере.
Пример 5. Как добавить предикат в лямбда-выражение
В предыдущем примереjava.util.function.Predicateдва или болееPredicateСинтезируйте один. Он предоставляет методы, подобные логическим операторам И и ИЛИ, называемыеand(),or(), который используется для передачиfilter()Условия метода совмещены. Например, чтобы получить все четырехбуквенные языки, начинающиеся с J, определите два отдельныхPredicateПример для каждого условия отдельно, затем используйтеPredicate.and()метод их объединения следующим образом
// 甚至可以用and()、or()逻辑函数来合并Predicate,
// 例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
.filter(startsWithJ.and(fourLetterLong))
.forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));
Точно так же вы также можете использоватьor()метод. В этом примере выделены следующие моменты:PredicateИспользуйте их как отдельные условия, а затем комбинируйте. Короче говоря, вы можете использовать традиционный командный способ JavaPredicateИнтерфейс, вы также можете в полной мере использовать лямбда-выражения, чтобы добиться вдвое большего результата с половиной усилий.
Пример 6. Пример Map and Reduce с использованием лямбда-выражений в Java 8
В этом примере представлена наиболее широко известная концепция функционального программирования — map. Позволяет преобразовывать объекты. Например, в этом примере мы будемcostBeforeTaxКаждый элемент списка преобразуется в значение после налогообложения. мы будемx -> x*xЛямбда-выражение передается методу map(), который применяет его к каждому элементу в потоке. Затем используйте forEach() для печати элементов списка. Используя класс коллектора Streaming API, вы можете получить все накладные расходы, включая налоги. Существуют такие методы, как toList(), которые объединяют результаты карты или любой другой операции. Поскольку сборщик выполняет терминальные операции с потоком, этот поток нельзя будет повторно использовать позже. Вы даже можете объединить все числа в одно, используя метод reduce() Streaming API, как будет показано в следующем примере.
// 不使用lambda表达式为每个订单加上12%的税
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
System.out.println(price);
}
// 使用lambda表达式
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);
вывод:
112.0
224.0
336.0
448.0
560.0
112.0
224.0
336.0
448.0
560.0
Пример 6.2, пример Map and Reduce с использованием лямбда-выражений в Java 8
В предыдущем примере вы можете видеть, что карта преобразует элементы класса коллекции (например, списка). есть еще одинreduce()Функция может объединять все значения в одно. Операции Map и Reduce являются основными операциями функционального программирования, и из-за своей функциональности сокращение также известно как операция свертки. Кроме того, сокращение не является новой операцией, возможно, вы уже используете ее. Аналогично в SQLsum(),avg()илиcount()Агрегатные функции на самом деле являются операциями сокращения, потому что они принимают несколько значений и возвращают одно значение. Функция reduceh(), определенная Streaming API, может принимать лямбда-выражение и объединять все значения. Такие классы, как IntStream, имеют что-то вродесреднее(), количество(), сумма()Встроенный метод для выполнения операции уменьшения, также естьmapToLong(), mapToDouble()метод преобразования. Это не ограничивает вас, вы можете использовать встроенные методы или определить свои собственные. В этом примере Java 8 Map Reduce мы сначала применяем12%НДС, затем использоватьreduce()способ подсчета суммы.
// 为每个订单加上12%的税
// 老方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
total = total + price;
}
System.out.println("Total : " + total);
// 新方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);
вывод:
Total : 1680.0
Total : 1680.0
Пример 7. Создание списка String путем фильтрации
Фильтрация — это обычная операция для Java-разработчиков при работе с большими коллекциями, и теперь фильтровать большие коллекции данных с помощью лямбда-выражений и потоковых API стало на удивление легко. поток обеспечиваетfilter()метод, который принимаетPredicateобъект, то есть вы можете передать лямбда-выражение в качестве логики фильтрации. Следующий пример фильтрации коллекции Java с помощью лямбда-выражения поможет понять.
// 创建一个字符串列表,每个字符串长度大于2
List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);
вывод:
Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]
Кроме того, существует распространенное заблуждение о методе filter(). В реальной жизни при фильтрации обычно отбрасываются части, но с помощью метода filter() получается новый список, каждый элемент которого соответствует принципу фильтрации.
Пример 8. Применение функции к каждому элементу списка
Обычно нам нужно использовать некоторую функцию для каждого элемента списка, например, умножение числа на единицу, деление на число или выполнение других операций. Эти операции подходят дляmap()метод, вы можете поместить логику преобразования в метод map() в виде лямбда-выражения, и вы можете преобразовать каждый элемент коллекции, как показано ниже.
// 将字符串换成大写并用逗号链接起来
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);
вывод:
USA, JAPAN, FRANCE, GERMANY, ITALY, U.K., CANADA
Пример 9. Скопируйте разные значения для создания подсписка
В этом примере показано, как использовать потокdistinct()метод дедупликации коллекции.
// 用所有不同的数字创建一个正方形列表
List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s, Square Without duplicates : %s %n", numbers, distinct);
вывод:
Original List : [9, 10, 3, 4, 7, 3, 4], Square Without duplicates : [81, 100, 9, 16, 49]
Пример 10. Вычисление максимума, минимума, суммы и среднего значений элементов множества
IntStream,LongStreamиDoubleStreamВ классе, ожидающем потоков, есть очень полезный метод, который называетсяsummaryStatistics(). может вернутьсяIntSummaryStatistics,LongSummaryStatisticsилиDoubleSummaryStatistics, который описывает различные сводные данные для элементов в потоке. В этом примере мы используем этот метод для вычисления максимального и минимального значений списка. он также имеетgetSum()иgetAverage()метод для получения суммы и среднего значения всех элементов списка.
//获取数字的个数、最小值、最大值、总和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
вывод:
Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9
Лямбда-выражения против анонимных классов
Теперь, когда лямбда-выражения собираются официально заменить анонимные внутренние классы в коде Java, необходимо провести их сравнительный анализ. Основное отличие заключается в ключевом словеthis. анонимный классthisключевые слова относятся к анонимным классам, а лямбда-выраженияthisКлючевое слово указывает на класс, который содержит лямбда-выражение. Еще одно отличие заключается в том, как они компилируются. Компилятор Java компилирует лямбда-выражения в закрытые методы класса. используя Яву 7invokedynamicинструкции байт-кода для динамического связывания этого метода.
Основы лямбда-выражений Java 8
10 лямбда-выражений Java, примеры потокового API
На данный момент мы рассмотрели 10 лямбда-выражений для Java 8, что является приличным объемом работы для начинающих, и вы можете сами запустить примеры программ, чтобы освоить их. Попробуйте изменить требования, чтобы создать свои собственные примеры для быстрого обучения. Я также хотел бы предложить вам использовать среду IDE Netbeans для практики лямбда-выражений, она имеет хорошую поддержку Java 8. При преобразовании кода в функциональный Netbeans вовремя подскажет. Анонимный класс легко преобразовать в лямбда-выражение, просто следуя советам Netbeans. Кроме того, если вы любите читать, то не забудьте заглянуть в книгу lambdas in Java 8, Practical Functional Programming (Java 8 Lambdas, pragmatic functional programming), Ричарда Уорбертона, или увидеть Java 8 Мэннинга в действии (Java 8 in Action), книга еще не вышла, но я думаю, что в Интернете есть бесплатный pdf-файл первой главы. Однако, прежде чем перейти к чему-либо еще, давайте рассмотрим ключевые моменты лямбда-выражений Java 8, методов по умолчанию и функциональных интерфейсов.
1) лямбда-выражения могут быть помещены только в следующий код: предопределенные функциональные интерфейсы, аннотированные @Functional, методы с абстрактной функцией или тип SAM (Single Abstract Method). Они называются целевыми типами лямбда-выражений и могут использоваться как возвращаемые типы или как аргументы для целевого лямбда-кода. Например, если метод принимает интерфейс Runnable, Comparable или Callable, существует единственный абстрактный метод, который можно передать в лямбда-выражении. Точно так же, если метод принимает интерфейс, объявленный в пакете java.util.function, например Predicate, Function, Consumer или Supplier, ему может быть передано лямбда-выражение.
2) Ссылка на метод может использоваться внутри лямбда-выражения только в том случае, если метод не изменяет параметры, предоставляемые лямбда-выражением. Лямбда-выражение в этом примере можно заменить ссылкой на метод, так как это всего лишь простой вызов метода с теми же параметрами.
list.forEach(n -> System.out.println(n));
list.forEach(System.out::println); // 使用方法引用
Однако, если есть какие-либо изменения параметров, вы не можете использовать ссылку на метод и вместо этого введите полное лямбда-выражение следующим образом:
list.forEach((String s) -> System.out.println("*" + s + "*"));
На самом деле объявление типа лямбда-параметра здесь можно опустить, и компилятор может вывести его из атрибута класса списка.
3) Статические, нестатические и локальные переменные могут использоваться внутри лямбды, что называется захватом переменных внутри лямбды.
4) Лямбда-выражения также называются замыканиями или анонимными функциями в Java, поэтому не удивляйтесь, если некоторые коллеги назовут это замыканием.
5) Лямбда-методы транслируются в частные методы внутри компилятора и отправляютсяinvokedynamicинструкции байт-кода для совершения звонков. Вы можете использовать JDK вjavapИнструмент для декомпиляции файлов классов. использоватьjavap -p илиjavap -c -vкоманда, чтобы взглянуть на байт-код, сгенерированный лямбда-выражением. Примерно это должно выглядеть так:
private static java.lang.Object lambda$0(java.lang.String);
6) у лямбда-выражений есть ограничение, то есть на них можно ссылаться толькоfinalилиfinalЛокальные переменные, что означает, что переменные, определенные за пределами области видимости, не могут быть изменены внутри лямбды.
List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { factor++; });
Compile time error : "local variables referenced from a lambda expression must be final or effectively final"
Кроме того, просто получить к нему доступ без изменений можно, например:
List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { System.out.println(factor*element); });
вывод:
4
6
10
14
Так что это больше похоже на неизменяемые замыкания, как в Python.
Это все 10 примеров лямбда-выражений в Java 8. Эта редакция станет крупнейшей в истории Java и окажет глубокое влияние на то, как разработчики Java будут использовать структуру коллекций в будущем. Я думаю, что одним из наиболее похожих изменений по масштабу стал выпуск Java 5, который принес множество преимуществ и улучшил качество кода, таких как: Generics, Enums, Autoboxing, Static Imports, Concurrency API и Variable Parameters. Вышеуказанные функции делают код Java намного чище, и я думаю, что лямбда-выражения сделают его еще лучше. Я с нетерпением жду разработки параллельных сторонних библиотек, которые могут упростить написание высокопроизводительных приложений.
Добро пожаловать в колонку"Идти в ногу с Java 8", делитесь превосходными руководствами и учебными пособиями по китайскому языку Java8 и приветствуйте высококачественные статьи.
Оригинальная ссылка:javarevisited
перевести:ImportNew.com - lemeilleur
Ссылка на перевод:www.importnew.com/16436.html