Синтаксис и новые функции в Java 8
1. Введение
С момента изобретения 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.
2. Методы по умолчанию для интерфейса
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);
}
}
3. Functional Interface (Функциональный интерфейс)
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("..");
}
}
Недействительно:
Действительно:
Недействительно:
Действительно:
4. Выражение Lambda
Сначала мы рассмотрим, как 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);
}
}
}
Результат запуска примера выше:
Apple
Cherry
Durian
Grapefruit
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);
}
}
5. Functional Interface API
Java 8 имеет большое количество доступных разработанных Functional Interface, они находятся в пакете java.util.function, здесь я покажу вам как использовать некоторые из этих интерфейсов, чтобы лучше понять выражение Lambda и их полезность.
java.util.function.Consumer
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));
}
}
java.util.function.Predicate
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));
}
}
java.util.function.Function
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);
}
java.util.function.Supplier
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
6. Method reference
Это функция, связанная с выражением 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.
Java Basic
- Настройте java compiler для обработки вашего Annotation (Annotation Processing Tool)
- Программирование на Java для группы с помощью Eclipse и SVN
- Руководство Java WeakReference
- Руководство Java PhantomReference
- Сжатие и декомпрессия в Java
- Настройка Eclipse для использования JDK вместо JRE
- Методы String.format() и printf() в Java
- Синтаксис и новые функции в Java 8
- Регулярные выражения Java
- Руководство Java Multithreading Programming
- Библиотеки Java JDBC Driver для различных типов баз данных
- Руководство Java JDBC
- Получить значения столбцов, автоматически возрастающих при вставлении (Insert) записи, используя JDBC
- Руководство Java Stream
- Руководство Java Functional Interface
- Введение в Raspberry Pi
- Руководство Java Predicate
- Абстрактный класс и Interface в Java
- Модификатор доступа (Access modifiers) в Java
- Руководство Java Enum
- Руководство Java Annotation
- Сравнение и Сортировка в Java
- Руководство Java String, StringBuffer и StringBuilder
- Обработка исключений Java - Java Exception Handling
- Руководство Java Generics
- Манипулирование файлами и каталогами в Java
- Руководство Java BiPredicate
- Руководство Java Consumer
- Руководство Java BiConsumer
- Что мне нужно для начала работы с Java?
- История Java и разница между Oracle JDK и OpenJDK
- Установить Java в Windows
- Установите Java в Ubuntu
- Установите OpenJDK в Ubuntu
- Установить Eclipse
- Установите Eclipse в Ubuntu
- Быстрое изучение Java для начинающих
- История бит и байтов в информатике
- Типы данных в java
- Битовые операции
- Команда if else в Java
- команды switch в Java
- Циклы в Java
- Массивы (Array) в Java
- JDK Javadoc в формате CHM
- Наследование и полиморфизм в Java
- Руководство Java Function
- Руководство Java BiFunction
- Пример Java encoding и decoding с использованием Apache Base64
- Руководство Java Reflection
- Java Удаленный вызов методов - Java RMI
- Руководство Программирование Java Socket
- Какую платформу я должен выбрать для разработки приложений Java Desktop?
- Руководство Java Commons IO
- Руководство Java Commons Email
- Руководство Java Commons Logging
- Понимание Java System.identityHashCode, Object.hashCode и Object.equals
- Руководство Java SoftReference
- Руководство Java Supplier
- Аспектно-ориентированное программирование Java с помощью AspectJ (AOP)
Show More
- Руководства Java Servlet/JSP
- Руководства Java Collections Framework
- Java API для HTML, XML
- Руководства Java IO
- Руководства Java Date Time
- Руководства Spring Boot
- Руководства Maven
- Руководства Gradle
- Руководства Java Web Services
- Руководства Java SWT
- Руководства JavaFX
- Руководства Oracle Java ADF
- Руководства Struts2 Framework
- Руководства Spring Cloud