Java 8 Killer Features. Часть 1

Не так давно зарелизилась Java 8 вот о ней я и хочу рассказать в данном посте. Мы рассмотрим все нововведения на примерах.

Уже давно по интернету ходили примеры c теми фишками который попадут в Java 8 но никто до конца не знал что и как будет в Java 8 все что было на слуху это Lambda выражения. Сейчас я постараюсь более понятно показать все новенькое в Java 8.

 

Что мы узнаем нового в Java 8?

1. Default методы для Interface

2. Lambda выражения

3. Функциональные Интерфейсы

4. Ссылки на методы и конструкторы

5. Lambda Scopes

 

1. Default методы для Interface

Довольно такие неплохому изменению подверглись Interfaces. Сразу пример:

public interface PeopleBirthday {

    String getFullName(String firstName, String lastName);

    default int getAge(int yearBirthday){
        return 2014 - yearBirthday;
    }

}

Как видите Java 8 позволяет нам добавлять не абстрактные реализации в интерфейс.

Сразу же появляется вопрос в чем отличие между абстрактным методом и default методом в интерфейсе?

Как известно Абстрактные классы содержащие abstract методы могут хранить состояние, а точнее переменные, в свою очередь интерфейсам это не под силу, максимум что могут интерфейсы это хранить константы.

Теперь когда вы реализуете интерфейс PeopleBirthday, то вы будите обязаны реализовать только метод getFullName(…) рассмотрим на примере:

Когда мы попытаемся реализовать интерфейс нам автоматически сгенерирует структуру только первого метода getFullName() так как он не является с default реализацией. Ну и собственно реализация:

public class PeopleBirthdayImpl implements PeopleBirthday {

    @Override
    public String getFullName(String firstName, String lastName) {
        return firstName + " " + lastName;
    }
}

Теперь  проверим:

public class Main {

    private static PeopleBirthday peopleBirthday = new PeopleBirthdayImpl();

    public static void main(String[] args) {
        System.out.println("-- Default Method Interface --");
        System.out.println(peopleBirthday.getFullName("Alexander", "Barchuk"));
        System.out.println(peopleBirthday.getAge(1992));
    }

}

Рузультат:

-- Default Method Interface --
Alexander Barchuk
22

 

2. Lambda выражения

На моё мнение Lambda Expressions — это штука вызывающая — Вау эффект. Начну с простого и полезного примера.

Представьте себе, что у вас есть список слов, который нужно отсортировать. В этом уроке я показывал как сортировать список объектов. Но мы рассмотрим на примере строк.

У нас есть следующий код:

public class SortListString {

    public static List<String> sort(List<String> st){
        Collections.sort(st, new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return a.compareTo(b);
            }
        });

        return st;
    }

}

В результате использования сортировки мы получим следующий результат:

public static void main(String[] args) {
    System.out.println("\n-- Lambda Expressions: Sort List --");
    List<String> st = Arrays.asList("Steve", "Alex", "Jim", "Bob");
    System.out.println("Before sort: " + st);
    System.out.println("After sort: " + SortListString.sort(st));
}

Результат:

Before sort: [Steve, Alex, Jim, Bob]
After sort: [Alex, Bob, Jim, Steve]

Теперь давйте переделаем метод SortListString.sort(st) с использованием Lambda Expressions:

В результате Intellij IDEA нам поправит наш код с использованием Lambda Expressions, но также вы можете сами написать данное выражение. Врезультате вы получите код метода уже с использованием Lambda выражения:

public static List<String> sort(List<String> st){
    Collections.sort(st, (a, b) -> a.compareTo(b));
    return st;
}

Сравним:

Но круто ведь :) Шесть строк кода вместить в одну.

 

3. Функциональные Интерфейсы

Функциональные интерфейсы тесно связаны с Lambda выражениями, как? Это мы сейчас разберем.

Каждому Lambda выражению соответствует тип, представленный интерфейсом. Именно этот интерфейс и называется Функциональным интерфейсом. Этот интерфейс должен содержать только один абстрактный метод (абстрактный метод — метод, который не имеет default реализации).

Скорее всего, вы уже подумали, как же это тупо, ведь я не всегда могу уследить и написать больше чем один метод. Согласен, но этого не будет, так как за этим будет следить специальная аннотация @FunctionalInterface.

Данная аннотация будет сигнализировать Java об ошибке, если вы попытаетесь написать больше одного абстрактного метода:

@FunctionalInterface
public interface Converter<F, T> {
    T convert(F f);
}

Вот что будет, если вы напишите более одного абстрактного метода в функциональном интерфейсе:

Пример использования Функционального Интерфейса:

public static void main(String[] args) {
    System.out.println("\n-- Functional Interface Using --");
    Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
    Integer convertAge = converter.convert("22");
    System.out.println("Age Convert: " + convertAge);
    System.out.println("Type: " + convertAge.getClass().getTypeName());
}

Результат:

-- Functional Interface Using --
Age Convert: 22
Type: java.lang.Integer

 

4. Ссылки на методы и конструкторы

Строку использования функционального интерфейса из предыдущего примера можно упростить с использованием ссылки на статический метод:

Converter<String, Integer> converter = (from) -> Integer.valueOf(from);

Теперь давайте упростим эту строку используя ссылку на статический метод:

Converter<String, Integer> converter = Integer::valueOf;

Намного проще я думаю :) Java 8 позволяет нам передавать ссылки на методы или конструкторы. Для того чтобы передать ссылку на метод либо на конструктор необходимо использовать два двоиточия ::

Давайте рассмотрим еще один пример для большего понимания:

public static void main(String[] args) {
    System.out.println("\n-- Method and Constructor References --");
    Something something = new Something();
    Converter<String, String> charConverter = something::startsWith;
    String converted = charConverter.convert("Java8");
    System.out.println("Result Char: " + converted);
}

Теперь давайте посмотрим как передавать ссылку на конструктор. Для начало создадим обьект Person:

public class Person {

    String firstName;
    String lastName;

    public Person() {}

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "Person{firstName: "+ firstName + ", lastName: " + lastName + "}";
    }

}

Затем создаем функциональный интерфейс для создания Person:

@FunctionalInterface
public interface PersonFactory<P extends Person> {
    P create(String firstName, String lastName);
}

Теперь рассмотрим, как передать ссылку на конструктор:

public static void main(String[] args) {
    System.out.println("\n-- Constructor References --");
    PersonFactory<Person> personFactory = Person::new;
    Person person = personFactory.create("Alexander", "Barchuk");
    System.out.println(person);
}

Как видите ничего сложного нет, с помощью Person::new мы создали ссылку на конструктор и в зависимости от структуру метода create() с функционального интерфейса будет выбран соответствующий конструктор.

 

5. Lambda Scopes (область видимости)

Кто помнит как получить доступ к переменной в анонимном объекте? Примерно так:

int num = 1;

new Runnable() {
    @Override
    public void run() {
        num = 2;
    }
};

Но при этом у нас не получится использовать переменную num так как она должна быть неизменимой и в этом случае мы получаем ошибку:

Для решения этой проблемы стоит сделать переменную num как статический (final) массив в нашем случае.

Теперь вернемся к Lambda выражениям, доступ к переменным из лямбда выражений также можно поучать как и в случае с анонимными объектами выше.

final int num = 1;
Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num);

Но final в этом случае не обязательный и без него все будет работать, но при условии, что переменная в дальнейшем не будет меняться.

Внутри Lambda выражений запрещено обращаться к default методам. Если брать за пример метод default getAge(…), то это работать не будет.

PeopleBirthday birthday = (a) -> getAge( a + 10);

 

Продолжение: Java 8 Killer Features. Часть 2

 

Статья написана с использованием материала winterbe.com.

Урок создан: 02 апреля 2014 | Просмотров: 19710 | Автор: Александр Барчук | Правила перепечатки


Добавить комментарий

Добавить комментарий

Ваш e-mail не будет опубликован.

Комментарии:

  • 02 февраля 2016 в 13:40

    Денис

    с ссылками на методы и конструктор я ничегошеньки не понял, почему объект «from» можно просто потерять? почему интерфейсу передается ссылка на конструктор? где здесь логика? если можно поподробнее о том что конкретно там происходит.

    • 11 марта 2016 в 10:54

      Аноним

      from — это аргумент, String.valueOf тоже принимает один аргумент, поэтому если если вся суть лямбды сводится к вызову к единственному методу, то можно объектом функционального интерфейса сделать ссылку на этот самый метод.
      Важно, чтобы совпадали сигнатуры метода функционального интерфейса и метода, на который мы берем ссылку (нужная перегрузка выбирается автоматически, соответствующая методу ф.интерфейса).
      Грубо говоря, это выходит вроде указателя на метод.