Рубрики
Без рубрики

Оператор двойного двоеточия в Java 8

Краткое руководство по оператору двойного двоеточия в Java 8 и тому, как его следует использовать на практике.

Автор оригинала: baeldung.

1. Обзор

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

Дальнейшее чтение:

Вопросы для интервью Java 8(+ Ответы)

Руководство По Java 8 Необязательно

Новые функции в Java 8

2. От Лямбды к оператору двойного двоеточия

С помощью лямбда-выражений мы видели, что код может стать очень кратким.

Например, для создания компаратора достаточно следующего синтаксиса:

Comparator c = (Computer c1, Computer c2) -> c1.getAge().compareTo(c2.getAge());

Затем с выводом типа:

Comparator c = (c1, c2) -> c1.getAge().compareTo(c2.getAge());

Но можем ли мы сделать приведенный выше код еще более выразительным и читабельным? Давайте посмотрим:

Comparator c = Comparator.comparing(Computer::getAge);

Мы использовали оператор :: в качестве сокращения для лямбд, вызывающих определенный метод – по имени. И конечным результатом, конечно, является еще более читаемый синтаксис.

3. Как Это Работает?

Очень просто, когда мы используем ссылку на метод – целевая ссылка помещается перед разделителем :: , а имя метода указывается после него.

Например:

Computer::getAge;

Мы смотрим на ссылку метода на метод getAge , определенный в классе Computer .

Затем мы можем работать с этой функцией:

Function getAge = Computer::getAge;
Integer computerAge = getAge.apply(c1);

Обратите внимание, что мы ссылаемся на функцию, а затем применяем ее к правильному аргументу.

4. Ссылки на методы

Мы можем хорошо использовать этот оператор в некоторых сценариях.

4.1. Статический метод

Во-первых, мы собираемся использовать статический метод утилиты :

List inventory = Arrays.asList(
  new Computer( 2015, "white", 35), new Computer(2009, "black", 65));
inventory.forEach(ComputerUtils::repair);

4.2. Метод экземпляра существующего объекта

Далее давайте рассмотрим интересный сценарий – ссылка на метод существующего экземпляра объекта .

Мы будем использовать переменную System . out – объект типа PrintStream , который поддерживает метод print :

Computer c1 = new Computer(2015, "white");
Computer c2 = new Computer(2009, "black");
Computer c3 = new Computer(2014, "black");
Arrays.asList(c1, c2, c3).forEach(System.out::print);

4.3. Метод экземпляра Произвольного объекта Определенного типа

Computer c1 = new Computer(2015, "white", 100);
Computer c2 = new MacbookPro(2009, "black", 100);
List inventory = Arrays.asList(c1, c2);
inventory.forEach(Computer::turnOnPc);

Как вы можете видеть, мы ссылаемся на метод turn On Pc не на конкретный экземпляр, а на сам тип.

В строке 4 метод экземпляра turn On Pc будет вызываться для каждого объекта inventory .

И это, естественно, означает, что – для c1 метод turnOnPc будет вызван на экземпляре Computer , а для c2 на экземпляре MacBookPro .

4.4. Супер-метод конкретного объекта

Предположим, у вас есть следующий метод в суперклассе Computer :

public Double calculateValue(Double initialValue) {
    return initialValue/1.50;
}

и этот в Macbook Pro подклассе:

@Override
public Double calculateValue(Double initialValue){
    Function function = super::calculateValue;
    Double pcValue = function.apply(initialValue);
    return pcValue + (initialValue/10) ;
}

Вызов метода calculate Value на экземпляре Macbook Pro :

macbookPro.calculateValue(999.99);

также вызовет также вызов calculate Value на компьютере суперклассе.

5. Ссылки на конструкторы

5.1. Создайте новый экземпляр

Ссылка на конструктор для создания экземпляра объекта может быть довольно простой:

@FunctionalInterface
public interface InterfaceComputer {
    Computer create();
}

InterfaceComputer c = Computer::new;
Computer computer = c.create();

Что делать, если у вас есть два параметра в конструкторе?

BiFunction c4Function = Computer::new; 
Computer c4 = c4Function.apply(2013, "white");

Если параметров три или более, необходимо определить новый функциональный интерфейс:

@FunctionalInterface 
interface TriFunction { 
    R apply(A a, B b, C c); 
    default  TriFunction andThen( Function after) { 
        Objects.requireNonNull(after); 
        return (A a, B b, C c) -> after.apply(apply(a, b, c)); 
    } 
}

Затем инициализируйте свой объект:

TriFunction  c6Function = Computer::new;
Computer c3 = c6Function.apply(2008, "black", 90);

5.2. Создайте массив

Наконец, давайте посмотрим, как создать массив Computer объектов с пятью элементами:

Function  computerCreator = Computer[]::new;
Computer[] computerArray = computerCreator.apply(5);

6. Заключение

Как мы начинаем видеть, оператор двойного двоеточия, введенный в Java 8, будет очень полезен в некоторых сценариях, особенно в сочетании с потоками.

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

Полный исходный код для примера доступен в этом проекте GitHub – это проект Maven и Eclipse, поэтому его можно импортировать и использовать как есть.