JavaRush /Java блог /Random /Java 8. Усе, що вам буде потрібно
Roman Beekeeper
35 рівень

Java 8. Усе, що вам буде потрібно

Стаття з групи Random

Підручник Java 8 Tutorial

"Java досі жива, і люди починають розуміти це."
Ласкаво просимо до мого представлення Java 8. Ця стаття проведе тебе крок за кроком вздовж усіх нових фіч з Java 7 до Java 8. За допомогою швидких і простих прикладів коду, ми зможемо вивчити як використовувати Default Interfaces, Method references і Repeatable annotations. Наприкінці статті ми познайомимося зі Stream API.
Java 8. Усе, що вам буде потрібно, - 1
Без зайвої балаканини – тільки код і коментарі до нього! Вперед!

Методи за замовчуванням для інтерфейсів

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 акаунт
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ