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);
formula.sqrt(16);
Інтерфейс
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);
Зауважте, що код буде також скомпільований, якщо
@FunctionalInterface
анотація буде пропущена.
Method and Constructor References
Приклад вище може бути також зроблено ще менше шляхом використання method references:
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);
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);
Погляньмо, як
::
працює з конструкторами. Для початку ми визначимо як приклад клас із різними конструкторами:
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 аккаунт
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ