Оператор с двоеточием в Java 8

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

1. обзор

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

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

Java 8 Интервью Вопросы (+ ответы)

Набор популярных вопросов об интервью, связанных с Java8, и, конечно, ответов.

Read more

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

Краткое и практическое руководство по дополнительному в Java 8

Read more

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

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

Read more

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

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

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

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. Статический метод

Во-первых, мы собираемся использоватьa static utility method:

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

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

Затем давайте посмотрим на интересный сценарий -referencing a method of an existing object instance.

Мы собираемся использовать переменную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);

Как видите, мы ссылаемся на методturnOnPc не на конкретный экземпляр, а на сам тип.

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

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

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

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

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

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

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

Вызов методаcalculateValue в экземпляреMacbookPro:

macbookPro.calculateValue(999.99);

также вызоветcalculateValue суперклассаComputer.

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, будет очень полезен в некоторых сценариях, особенно в сочетании с Streams.

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

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