Синтаксис и новые функции в Java 8
View more Tutorials:

С момента изобретения Java до обновления синтаксиса и функций, стоит отметить несколько важных этапов:
- Java 1.0: Начало языка программирования.
- Java 1.1, 1.2, 1.3, 1.4 не так много изменений в синтаксисе и функции.
- Java 1.5 (Или Java 5) произошли большие изменения вместе с добавлением некоторых новых понятий.
- Generic (Общий)
- Autoboxing/Unboxing (Упаковка/распаковка)
- Обновление функции цикла for ("foreach").
- Виды перечисления. (Type-safe enumerations).
- Varargs (Переменные аргументы)
- Статический импорт (Static import)
- Metadata (Метаданные)
- Java 6,7 нет больших изменений в языке.
- Java 8 произошло большое изменение в языке, наряду с добавлением некоторых новых понятий и функций:
- Методы по умолчанию для интерфейса(Default interface methods)
- Lambda выражения (Lambda expressions)
- Справочные методы
- Повторяющиеся аннотации (Repeatable annotations)
- Поток (stream)
В этой статье я ознакомлю вас с функциями и синтаксисами Java 8.
Java 8 позволяет вам добавить невизуальный метод в интерфейс, используя ключевок слово default. Эти методы понимаются как расширенные методы. Это ваш первый пример:
Formula.java
package org.o7planning.tutorial.j8.itf; public interface Formula { // Объявить абстрактный метод. double calculate(int a); // Объявить неабстрактный метод. // Использовать ключевок слово default. // (Функция считает квадратный корень числа). default double sqrt(int a) { return Math.sqrt(a); } }
И класс FormulaImpl реализует interface Formula.
FormulaImpl.java
package org.o7planning.tutorial.j8.itf; // Класс выполняет interface Formula. public class FormulaImpl implements Formula { // Нужно только выполнить абстрактный метод Formula. @Override public double calculate(int a) { return a * a - a; } }
FormulaTest.java
package org.o7planning.tutorial.j8.itf; public class FormulaTest { public static void main(String[] args) { Formula formula = new FormulaImpl(); // ==> 5 double value1 = formula.sqrt(25); System.out.println("Value1 = " + value1); // ==> 600 double value2 = formula.calculate(25); System.out.println("Value2 = " + value2); } }
Java8 считает, что интерфейсы имеют один абстрактный метод - это Functional Interface (функциональный интерфейс). Вы можете использовать аннотацию @FuctionalInterface, чтобы отметить ваш интерфейс как Функциональный интерфейс, это не обязательно. Однако компилятор (complier) Java уведомит вас об ошибке, если по нечаянно добавить другой абстрактный метод в интерфейс, отмеченный этой аннотацией.
Ниже являются некоторые практические примеры с @FunctionalInterface:
Приведенный ниже пример является действительным FunctionalInterface, потому что он имеет только один абстрактный метод.
Foo.java
package org.o7planning.tutorial.j8.funcitf; @FunctionalInterface public interface Foo { void something(); default void defaultMethod() { System.out.println(".."); } }
Недействительно:

Действительно:

Недействительно:

Действительно:

Сначала мы рассмотрим, как Java до версии 8 организует Collection.
Смотрите так же:
SortBefore8Example.java
package org.o7planning.tutorial.j8.lambda; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; public class SortBefore8Example { public static void main(String[] args) { // Список фруктов. List<String> fruits = Arrays.asList("Grapefruit", "Apple", "Durian", "Cherry"); // Использовать утилитарный метод Collections // чтобы перераспределить коллекцию. // Предоставить Comparator (Компаратор). Collections.sort(fruits, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } }); for (String fruit : fruits) { System.out.println(fruit); } } }
Результат запуска примера выше:

Java 8 считает что интерфейсы, имеют только один метод, который является функциональным интерфейсом (Functional Interface). Соответственно, при реализации интерфейса вам нужно всего лишь написать метод, реализующий этот единственный абстрактный метод. Comparator - это интерфейс, имеющий только один абстрактный метод, и это функциональный интерфейс (Functional Interface). Вы можете переписать приведенный выше пример с синтаксисом Lambda Java 8:
SortJava8Example.java
package org.o7planning.tutorial.j8.lambda; import java.util.Arrays; import java.util.Collections; import java.util.List; public class SortJava8Example { public static void main(String[] args) { // Список фруктов. List<String> fruits = Arrays.asList("Grapefruit", "Apple", "Durian", "Cherry"); // Использовать утилитарный метод Collections чтобы перераспределить список выше. // Предоставить Comparator (Компаратор) для второго параметра метода. // Так как Comparator это интерфейс с единственным методом. // ==> Можно сократить с выражением Lambda. // Не нужно писать название интерфейса, // Не нужно писать название метода. Collections.sort(fruits, (String o1, String o2) -> { return o1.compareTo(o2); }); for (String fruit : fruits) { System.out.println(fruit); } } }
В блоке команд, если есть только одна команды, вы можете удалить { }, и вы можете написать этот раздел кода более кратким образом.
Collections.sort(fruits, (String o1, String o2) -> o1.compareTo(o2) );
Даже компилятор (compilier) Java достаточно умен, чтобы определить, какой вид элементов вам нужно организовать, в этом примере это вид String. Таким образом, компаратор (Comparator), безусловно, означает сравнение видов данных String. Вы можете написать это более кратко.
Collections.sort(fruits, (o1, o2) -> o1.compareTo(o2));
Другие примеры с выражением Lambda.
Converter.java
package org.o7planning.tutorial.j8.lambda; @FunctionalInterface public interface Converter<F, T> { T convert(F from); }
Используйте интерфейс Converter в виде Java до версии 8 (это означает, что не используйте Lambda).
ConverterBefore8Example.java
package org.o7planning.tutorial.j8.lambda; public class ConverterBefore8Example { public static void main(String[] args) { // Инициализировать объект Converter. Converter<String, Integer> converter = new Converter<String, Integer>() { @Override public Integer convert(String from) { return Integer.parseInt(from); } }; // ==> 100 Integer value = converter.convert("0100"); System.out.println("Value = " + value); } }
Использование выражения Lambda на Java 8:
ConveterJava8Example.java
package org.o7planning.tutorial.j8.lambda; public class ConveterJava8Example { public static void main(String[] args) { // Converter (Конвертер) является FunctionalInterface // Использовать синтаксис в Java 8 (Lambda) // В случае: Создать объект напрямую из FunctionalInterface. Converter<String, Integer> converter1 = (String from) -> { return Integer.parseInt(from); }; // ==> 100 Integer value1 = converter1.convert("0100"); System.out.println("Value1 = " + value1); // Или проще: Converter<String, Integer> converter2 = (from) -> Integer .parseInt(from); // ==> 200 Integer value2 = converter2.convert("00200"); System.out.println("Value2 = " + value2); // Если метод имеет только один параметр, можно пропустить (). Converter<String, Integer> converter3 = from -> Integer .parseInt(from); // ==> 300 Integer value3 = converter3.convert("00300"); System.out.println("Value3 = " + value3); } }
Java 8 имеет большое количество доступных разработанных Functional Interface, они находятся в пакете java.util.function, здесь я покажу вам как использовать некоторые из этих интерфейсов, чтобы лучше понять выражение Lambda и их полезность.
Consumer (Потребитель) - это доступный функциональный интерфейс Java 8. Он имеет единственный абстрактный метод, принимающий входной параметр, и этот метод ничего не возвращает.
Consumer.java
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Consumer<T> { // Метод принимает один входной параметр и ничего не возвращает. void accept(T t); }
Использование метода List.forEach(Consumer):
// java.util.List extends java.util.Collection (extends Iterable) // Interface java.util.Iterable: default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }
ConsumerExample.java
package org.o7planning.tutorial.j8.api; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; public class ConsumerExample { // Использовать метод List.forEach(Consumer) с синтаксисом в Java < 8. // Напечатать список элементов List. public static void beforeJ8() { List<String> list = Arrays.asList("a", "b", "c", "a1", "a2"); list.forEach(new Consumer<String>() { @Override public void accept(String t) { System.out.println(t); } }); } // Использовать метод List.forEach(Consumer) с синтаксисом в Java 8. // Используя выражение Lambda. public static void java8Consumer() { List<String> list = Arrays.asList("a", "b", "c", "a1", "a2"); list.forEach((String t) -> { System.out.println(t); }); } // Использовать метод List.forEach(Consumer) с синтаксисом в Java 8. // Используя выражение Lambda. // (Проще) public static void java8ConsumerMoreSimple() { List<String> list = Arrays.asList("a", "b", "c", "a1", "a2"); list.forEach((String t) -> System.out.println(t)); } }
Predicate - это доступный функциональный интерфейс Java 8. Он имеет единственный абстрактный метод, принимающий входной параметр, и метод возвращает значение boolean (true / false). Этот метод используется для оценки того, подходит ли входной параметр для чего-то логического или нет.
Predicate.java
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Predicate<T> { // Оценивает входной параметр и возвращает true или false. boolean test(T t); }
В следующем примере мы будем фильтровать список целых нечетных чисел, используя Predicate, принимающий форму Java8 и предыдущих версий.
PredicateExample.java
package org.o7planning.tutorial.j8.api; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; import java.util.stream.Stream; public class PredicateExample { // Использовать метод Stream.filter(Predicate<T>) по синтаксису в Java < 8. // Отфильтровать список целых чисел и напечатать нечетные числа. public static void beforeJ8() { List<Integer> list = Arrays.asList(1, 4, 5, 1, 7, 8); // Stream содержит элементы списка выше. Stream<Integer> stream = list.stream(); // Новый Stream содержит только нечетные числа. Stream<Integer> stream2 = stream.filter(new Predicate<Integer>() { @Override public boolean test(Integer t) { return t % 2 == 1; } }); } // Использовать метод Stream.filter(Predicate<T>) по синтаксису в Java >= 8. // Отфильтровать список целых чисел и напечатать нечетные числа. // Используя выражение Lambda. public static void java8Predicate() { List<Integer> list = Arrays.asList(1, 4, 5, 1, 7, 8); // Stream содержит элементы списка выше. Stream<Integer> stream = list.stream(); // Новый Stream содержит только нечетные числа. Stream<Integer> stream2 = stream.filter(t -> { return t % 2 == 1; }); // Stream.forEach(Consumer<T>) stream2.forEach(t -> System.out.println(t)); } // Еще проще и кратче. public static void java8ConsumerMoreSimple() { List<Integer> list = Arrays.asList(1, 4, 5, 1, 7, 8); // Stream содержит элементы списка выше. Stream<Integer> stream = list.stream(); stream.filter(t -> t % 2 == 1).forEach(t -> System.out.println(t)); } }
Function - это доступный функциональный интерфейс Java 8. Он имеет единственный абстрактный метод, принимающий входной параметр, и метод возвращает другой объект.
Function.java
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Function<T, R> { // Этот метод принимает один параметр. // Возвращает одно значение R apply(T t); }
Пример: Дан список String, распечатайте String в списке с заглавными буквами
FunctionExample.java
package org.o7planning.tutorial.j8.api; import java.util.Arrays; import java.util.List; import java.util.function.Function; import java.util.stream.Stream; public class FunctionExample { // Использовать метод Stream.map(Function) с синтаксисом в Java < 8. // Напечатать список элементов List. public static void beforeJ8() { List<String> list = Arrays.asList("a", "c", "B", "e", "g"); // Stream содержит элементы списка. Stream<String> stream = list.stream(); // Stream.map(Function): // <R> Stream<R> map(Function<? super T, ? extends R> mapper); // Возвращает новый Stream, с измененными элементами. Stream<String> streamUpper = stream.map(new Function<String, String>() { @Override public String apply(String t) { return t == null ? null : t.toUpperCase(); } }); streamUpper.forEach(t -> System.out.println(t)); } public static void java8Function() { List<String> list = Arrays.asList("a", "c", "B", "e", "g"); // Stream содержит элементы списка. Stream<String> stream = list.stream(); stream.map(t -> t == null ? null : t.toUpperCase()).forEach(t -> System.out.println(t)); } public static void main(String[] args) { beforeJ8(); java8Function(); } }
Аналогичные функциональные интерфейсы (Functional interface):
- java.util.function.IntFunction<R>
- java.util.function.DoubleFunction<R>
- java.util.function.LongFunction<R>
@FunctionalInterface public interface IntFunction<R> { R apply(int value); } @FunctionalInterface public interface LongFunction<R> { R apply(long value); } @FunctionalInterface public interface DoubleFunction<R> { R apply(double value); }
Supplier - это доступный функциональный интерфейс Java 8. Он имеет единственный абстрактный метод без параметра, а метод возвращает объект.
Supplier.java
package java.util.function; @FunctionalInterface public interface Supplier<T> { // Этот метод не имеет параметров. // Но возвращает результат. T get(); }
SupplierExample.java
package org.o7planning.tutorial.j8.api; import java.util.function.Supplier; public class SupplierExample { // Метод с параметром Supplier<String>. public static void display(Supplier<String> supp) { System.out.println(supp.get()); } // Не используя выражение Lambda. public static void beforeJ8() { display(new Supplier<String>() { @Override public String get() { return "Hello"; } }); display(new Supplier<String>() { @Override public String get() { return "World"; } }); } // Используя выражение Lambda. public static void java8Supplier() { display(() -> { return "Hello"; }); display(() -> { return "World"; }); } // Используя выражение Lambda. // (Еще кратче). public static void java8SupplierShortest() { display(() -> "Hello"); display(() -> "World"); } public static void main(String[] args) { beforeJ8(); System.out.println("-----------"); java8SupplierShortest(); } }
Аналогичные функциональные интерфейсы (Functional interface):
- java.util.function.BooleanSupplier
- java.util.function.IntSupplier
- java.util.function.DoubleSupplier
- java.util.function.LongSupplier
Это функция, связанная с выражением Lambda. Она позволяет вам ссылаться на другие конструкторы или методы, не выполняя их. Ссылки на методы (method references) и Lambda аналогичны тем, что они оба требуют один вид цели, это совместимый функциональный интерфейс.
Java 8 позволяет передавать ссылку метода или конструктора с помощью ключевого слова ::
Прежде чем смотреть на детали, давайте посмотрим простой пример.
MyFunction - это функциональный интерфейс. Он определяет метод, который имеет два параметра: int a и b и возвращает значение int.
MyFunction.java
package org.o7planning.tutorial.j8.mref; @FunctionalInterface public interface MyFunction { // Этот метод имеет 2 параметра a, b и возвращает вид int. public int doSomething(int a, int b); }
MyMathUtils - это класс с двумя статическими методами, используемыми для вычисления суммы и разность двух чисел int.
MyMathUtils.java
package org.o7planning.tutorial.j8.mref; public class MyMathUtils { // Этот метод имеет 2 параметра a, b и возвращает вид int. // Этот метод имеет другое название, // но структура похожа на MyFunction.doSomething(int,int). public static int sum(int a, int b) { return a + b; } public static int minus(int a, int b) { return a - b; } }
MethodReferenceExample.java
package org.o7planning.tutorial.j8.mref; public class MethodReferenceExample { // Третий параметр данного метода является MyFunction (Functional Interface). // При использовании данного метода: // Вы можете передать ссылку метода для третьего параметра. // (Методы должны быть одного вида с MyFunction). public static int action(int a, int b, MyFunction func) { return func.doSomething(a, b); } public static void main(String[] args) { int a = 100; int b = 30; // Передать ссылку метода MyMathUtils.sum. int c = action(a, b, MyMathUtils::sum);// ==> 130. System.out.println("c = " + c); // Передать ссылку метода MyMathUtils.minus. int d = action(a, b, MyMathUtils::minus);// ==> 70 System.out.println("d = " + d); // Передать ссылку метода Math.subtractExact. int e = action(a, b, Math::subtractExact);// ==> 70 System.out.println("e = " + e); // Передать ссылку метода Math.min. int f = action(a, b, Math::min);// ==> 30 System.out.println("f = " + f); } }
По примеру выше, вы можете увидеть способ использования ключевого слова :: чтобы передать ссылку метода. Если вы вызываете метод, а в том методе имеется аргумент Functional Interface, вы можете передать метод ссылки со структурой похожей на структуру метода определения в Functional interface.