Автор оригинала: David Landup.
Вступление
Самым сладким синтаксическим сахаром , добавленным в Java до сих пор, безусловно, являются Лямбда-выражения .
Java-это подробный язык, и это может помешать производительности и удобочитаемости. Сокращение шаблонного и повторяющегося кода всегда было популярной задачей среди разработчиков Java, и, как правило, требуется чистый, читаемый и лаконичный код.
Лямбда-выражения избавили от необходимости вводить громоздкий шаблонный код, когда речь заходит о некоторых распространенных задачах, позволяя разработчикам вызывать их без принадлежности к классу и передавать их так, как если бы они были объектами.
Эти выражения широко используются в API Java Streams и фреймворке Spring Web Flux для создания реактивных динамических приложений.
Еще одна, действительно полезная функция, добавленная в Java 8, – это ссылки на методы , которые делают Лямбда-выражения намного более краткими и простыми, вызывая (ссылаясь) методы с использованием имени метода, когда Лямбда-выражение использовалось бы просто для вызова метода.
Ссылки на методы
Ссылки на методы-это, по сути, сокращенные лямбда-выражения, используемые для вызова методов.
Они состоят из двух частей:
Class::method;
И распространенным примером может быть распечатка результатов, скажем, подписки на службу издателя или поток Java:
someCodeChain.subscribe(System.out::println);
Давайте рассмотрим пример императивного кода, который затем мы обратимся к функциональному коду с помощью лямбда-выражений, а затем, наконец, сократим с помощью ссылок на методы.
Мы проведем простой урок:
public class Employee { private int id; private String name; private int wage; private String position; // Constructor, getters and setters @Override public String toString() { return "Name: " + name + ", Wage: " + wage + ", Position: " + position; } public int compareTo(Employee employee) { if (this.wage <= employee.wage) { return 1; } else { return -1; } } }
Если бы мы сформировали этот класс в коллекцию, такую как ArrayList
, мы не смогли бы отсортировать его с помощью служебного метода .sort ()
, поскольку он не реализует Сопоставимый
интерфейс.
Что мы можем сделать, так это определить новый компаратор
для этих объектов при вызове метода .sort()
:
Employee emp1 = new Employee(1, "David", 1200, "Developer"); Employee emp2 = new Employee(2, "Tim", 1500, "Developer"); Employee emp3 = new Employee(3, "Martha", 1300, "Developer"); ArrayListemployeeList = new ArrayList<>(); employeeList.add(emp1); employeeList.add(emp2); employeeList.add(emp3); Collections.sort(employeeList, new Comparator () { public int compare(Employee emp1, Employee emp2) { return emp1.compareTo(emp2); } }); System.out.println(employeeList);
Запуск этого кода приведет к:
[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
Здесь класс anonymous ( Компаратор
) определяет критерии сравнения. Мы можем сделать это намного проще и короче, используя лямбда-выражение:
Collections.sort(employeeList, (e1, e2) -> e1.compareTo(e2));
Запуск этого фрагмента кода приведет к:
[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
Опять же, поскольку все, что мы делаем с этим лямбда-выражением, – это вызываем один метод, мы можем ссылаться только на этот метод:
Collections.sort(employeeList, Employee::compareTo);
И это также даст:
[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
Типы ссылок на методы
Ссылки на методы могут использоваться в нескольких различных сценариях:
- Статические методы:
Класс::Имя статического метода
- Методы экземпляра конкретных объектов:
объект::имя метода экземпляра
- Методы экземпляра произвольных объектов:
Класс::имя метода
- Ссылка на конструктор:
Класс::новый
Давайте рассмотрим все эти типы на нескольких простых примерах.
Ссылки на статические методы
Вы можете ссылаться на любой статический
метод класса, просто вызвав содержащий его класс с именем метода.
Давайте определим класс с помощью статического
метода, а затем сошлемся на него из другого класса:
public class ClassA { public static void raiseToThePowerOfTwo(double num) { double result = Math.pow(num, 2); System.out.println(result); } }
А теперь, из другого класса, давайте воспользуемся статическим
служебным методом:
public class ClassB { public static void main(String[] args) { ListintegerList = new ArrayList<>(); integerList.add(new Double(5)); integerList.add(new Double(2)); integerList.add(new Double(6)); integerList.add(new Double(1)); integerList.add(new Double(8)); integerList.add(new Double(9)); integerList.forEach(ClassA::raiseToThePowerOfTwo); } }
Запуск этого фрагмента кода приведет к:
25.0 4.0 36.0 1.0 64.0 81.0
Существует множество классов Java, которые предлагают статические
служебные методы, которые можно использовать здесь. В нашем примере мы использовали пользовательский метод, хотя и не очень полезный в данном случае.
Методы экземпляра конкретных объектов
Git Essentials
Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!
Вы можете вызвать метод из определенного экземпляра объекта, сославшись на метод с помощью ссылочной переменной объекта.
Чаще всего это иллюстрируется с помощью специального компаратора. Мы будем использовать тот же Сотрудник
класс, что и раньше, и тот же список, чтобы подчеркнуть разницу между этими двумя:
public class Employee { private int id; private String name; private int wage; private String position; // Constructor, getters and setters @Override public String toString() { return "Name: " + name + ", Wage: " + wage + ", Position: " + position; } public int compareTo(Employee employee) { if (this.wage <= employee.wage) { return 1; } else { return -1; } } }
Теперь давайте определим Пользовательский компаратор
:
public class CustomComparator { public int compareEntities(Employee emp1, Employee emp2) { return emp1.compareTo(emp2); } }
И, наконец, давайте составим список и отсортируем его:
Employee emp1 = new Employee(1, "David", 1200, "Developer"); Employee emp2 = new Employee(2, "Tim", 1500, "Developer"); Employee emp3 = new Employee(3, "Martha", 1300, "Developer"); ArrayListemployeeList = new ArrayList<>(); employeeList.add(emp1); employeeList.add(emp2); employeeList.add(emp3); // Initializing our CustomComparator CustomComparator customComparator = new CustomComparator(); // Instead of making a call to an arbitrary Employee // we're now providing an instance and its method Collections.sort(employeeList, customComparator::compareEntities); System.out.println(employeeList);
Выполнение этого кода также приведет к:
[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
Основное отличие заключается в том, что , добавив еще один слой через Пользовательский компаратор
, мы можем добавить больше функций для сравнения и удалить их из самого класса. Такой класс, как Сотрудник
, не должен быть обременен сложной логикой сравнения, и это приводит к более чистому и удобочитаемому коду.
С другой стороны, иногда мы не хотим определять пользовательские компараторы, и введение одного из них просто слишком хлопотно. В таких случаях мы бы вызвали метод из произвольного объекта определенного типа, как показано в следующем разделе.
Методы экземпляра произвольных объектов
Этот пример уже был показан в начале статьи, когда мы свели императивный подход к функциональному подходу с помощью лямбда-выражений.
Хотя, для пущей убедительности, поскольку этот подход используется очень часто, давайте рассмотрим другой пример:
ListintegerList = new ArrayList<>(); integerList.add(new Integer(5)); integerList.add(new Integer(2)); integerList.add(new Integer(6)); integerList.add(new Integer(1)); integerList.add(new Integer(8)); integerList.add(new Integer(9)); // Referencing the non-static compareTo method from the Integer class Collections.sort(integerList, Integer::compareTo); // Referencing static method integerList.forEach(System.out::print);
Запуск этого фрагмента кода приведет к:
125689
Хотя это может показаться, что это то же самое, что вызов статического метода, это не так. Это эквивалентно вызову лямбда – выражения:
Collections.sort(integerList, (Integer a, Integer b) -> a.compareTo(b));
Здесь различие более очевидно. Если бы мы вызвали метод static
, это выглядело бы так:
Collections.sort(integerList, (Integer a, Integer b) -> SomeClass.compare(a, b));
Ссылки На Конструкторы
Вы можете ссылаться на конструктор класса так же, как на статический
метод.
Вы можете использовать ссылку на конструктор вместо классического создания экземпляра класса:
// Classic instantiation Employee employee = new Employee(); // Constructor reference Employee employee2 = Employe::new;
В зависимости от контекста, если присутствует несколько конструкторов, при ссылке будет использоваться соответствующий:
Streamstream = names.stream().map(Employee::new);
Из-за потока имен, если присутствует Сотрудник(строковое имя)
конструктор, он будет использоваться.
Другой способ, которым вы могли бы использовать ссылки на конструктор, – это когда вы хотите отобразить поток в массив, сохраняя при этом определенный тип. Если бы вы просто сопоставили его , а затем вызвали toArray ()
, вы получили бы массив Объектов
вместо вашего конкретного типа.
Если бы мы попытались, скажем:
Employee[] employeeArray = employeeList.toArray();
Конечно, мы были бы встречены ошибкой компилятора, так как мы .toArray()
возвращает массив Объекта
s. Приведение этого также не поможет:
Employee[] employeeArray = (Employee[]) employeeList.toArray();
Но на этот раз это будет исключение во время выполнения – ClassCastException
.
Мы можем избежать этого с помощью:
// Making a list of employees ListemployeeList = Arrays.asList("David", "Scott"); // Mapping a list to Employee objects and returning them as an array Employee[] employeeArray = employeeList.stream().map(Employee::new).toArray(Employee[]::new); // Iterating through the array and printing information for (int i = 0; i < employeeArray.length; i++) { System.out.println(employeeArray[i].toString()); }
И с этим мы получаем результат:
Name: David, Wage: 0, Position: null Name: Scott, Wage: 0, Position: null
Вывод
Ссылки на методы – это тип лямбда-выражений, которые используются для простой ссылки на метод в их вызове. С их помощью написание кода может быть намного более кратким и читабельным.
Лямбда-выражения познакомили разработчиков Java с более функциональным подходом к программированию, который позволяет им избегать написания подробного кода для простых операций.