Java 8 Tutorial
"Java до сих пор жива, и люди начинают понимать это."Добро пожаловать в мое представление Java 8. Эта статья проведет тебя шаг за шагом дволь всех новый фич с Java 7 в Java 8. При помощи быстрых и простых примеров кода, мы сможем выучить как использовать Default Interfaces, Method references и Repeatable annotations. В конце статьи мы познакомимся со Stream API.
Без лишней болтовни — только код и комментарии к нему! Вперед!
Default Methods for Interfaces
Java 8 позволяет нам добавлять не абстрактные методы(которые реализованы) в интерфейсы путем добавления ключевого словаdefault
. Эта возможность так же известна как Extention Methods. Ниже представлен первый пример:
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
Помимо абстрактного метода calculate
, в интерфейсе Formula
так же определен дефолтный метод sqrt
. Классы, реализующие этот интерфейс, должны только реализовать метод calculate
. Дефолтный метод sqrt
может быть использован "из коробки".
Formula formula = new Formula() {
@Override
public double calculate(int a) {
return sqrt(a * 100);
}
};
formula.calculate(100); // 100.0
formula.sqrt(16); // 4.0
Интерфейс Formula
реализован как как анонимный класс. Код избыточный: 6 строк для реализации sqrt(a * 100)
. Как мы увидим в следующей секции, есть более красивый способ для реализации одиночного метода в Java 8.
Lambda expressions
Давайте начнем с простого примера сортировки списка строк в предыдущих версиях Java:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
Статический метод Collections.sort
принимает список и сомпаратор в том порядке, в котором нужно сортировать список. Всегда можно создать анонимный класс компаратора и передать его.
Вместо создания анонимного класса, в Java 8 можно сделать более короткую запись, лямбда выражения.
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
Как вы видите код намного короче и проще для чтения. Но это можно сделать еще короче:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
Для тела с одной строкой можно пропустить {}
и слово return
. Но можно сделать это еще короче:
Collections.sort(names, (a, b) -> b.compareTo(a));
Java компилятор знает о типах аргументов, так что их можно пропустить также. Давайте копнем глубже в лямбда выражения и поймем как их можно использовать.
Functional Interfaces
Как лямбда выражения вписываются в систему Java типов? Каждая лямбда соответствует типу, описываемому в интерфейсе. Поэтому функциональный интерфейс должен содержать только один абстрактный метод. Каждое лямбда выражение этого типа будет соответствовать этому абстрактному методу. С момента того, что дефолтные методы не абстрактные, поэтому вы вольны создать дефолтные методы в функциональные интерфейсы сколько нужно. Мы также можем использовать произвольные интерфейсы как лямбда выражения в случае если в этом интерфейсе только один абстрактный метод. Чтобы соответствовать этим требованиям нужно добавить@FucntionalInterface
аннотацию. Компилятор знает о ней и выбросит исключение, если если захотите поставить больше одного абстрактного метода. Пример:
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted); // 123
Держите в уме, что код будет также скомпилирован, если @FunctionalInterface
аннотация будет пропущена.
Method and Constructor References
Пример выше может быть также сделан еще более меньше путем использования method references:
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted); // 123
Java 8 позволяет вам передавать ссылки на метод или констрктор путем добавления ::
. Пример выше показывает как можно ссылаться на статический метод, не смотря на это мы также можем ссылаться на нестатические методы:
class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted); // "J"
Давайте посмотрим, как ::
работает с конструкторами. Для начала мы определим в качестве примера класс с разными конструкторами:
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
Далее определеим интерфейс для создания новых объектов:
interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
}
Вместо того, чтобы реализовывать фабрику по создания, мы соединим воедино все это спомощью ::
для конструкторов:
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
Мы создали ссылку на конструктор через Person::new
. Java компилятор автоматически выберет правильный конструктор, который соответствует сигнатуре метода PersonFactory.create
.
... Продолжение следует. К сожалению я не нашел, как можно сохранить черновик статьи, а это реально странно, а сеанс времени на перевод окончен - поэтому доделаю позже. Всем знающим и понимающим английский — Оринигалая Статья. Если есть предложения по исправлению перевода — пишите любыми доступным вам способом.
Мой Github aккаунт
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ