Підручник Java 8 Tutorial
"Java досі жива, і люди починають розуміти це."Ласкаво просимо до мого представлення Java 8. Ця стаття проведе тебе крок за кроком вздовж усіх нових фіч з Java 7 до Java 8. За допомогою швидких і простих прикладів коду, ми зможемо вивчити як використовувати Default Interfaces, Method references і Repeatable annotations. Наприкінці статті ми познайомимося зі Stream API.
Без зайвої балаканини – тільки код і коментарі до нього! Вперед!
Методи за замовчуванням для інтерфейсів
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 = 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.
Лямбда-вирази
Давайте почнемо з простого прикладу сортування списку рядків у попередніх версіях Java:
List<String> names = Arrays.asList("петр", "анна", "майк", "ксеня");
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 компілятор знає про типи аргументів, тож їх можна пропустити також. Давайте зануримося глибше в лямбда вирази і зрозуміємо як їх можна використовувати.
Функціональні інтерфейси
Як лямбда вирази вписуються в систему 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 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 акаунт
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ