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. ... Продолжение следует. К сожалению я не нашел, как можно сохранить черновик статьи, а это реально странно, а сеанс времени на перевод окончен - поэтому доделаю позже. Всем знающим и понимающим английский — Оринигалая Статья. Если есть предложения по исправлению перевода — пишите любыми доступным вам способом.