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

View more Tutorials:

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);
		}

	}
}
Результат запуска примера выше:
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 и их полезность.

5.1- 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));
	}
	
}

5.2- 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));
	}

}

5.3- 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);
}

5.4- 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.

View more Tutorials: