Java 9 — Уже здесь!

На этой неделе, 21 сентября, наконец-то зарелизилась Девятка. Хех, Джава комьюнити не пошло на поводу у Майкрасофта и Эпла, которые решили пропустить из своих продуктов цифру 9 (привет, Винда и Айфоны) и всё-таки выпустило java 9.

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

В Девятке есть лишь одна главная фича, которая, к сожалению, весьма спорна. Речь идёт про Project Jigsaw. Это нативная реализация модулей в платформе. Читать подробнее можно тут — http://openjdk.java.net/projects/jigsaw/.

Про модули пока не хочется говорить. Вся внутрення структура Джавы уже переписана на использование модулей. Но на сколько всё это дело взлетит — не ясно. Возможно, в пользовательском коде нам не придётся использовать jigsaw. Причин этому очень много. Во-первых, модули не решают jar hell problem. Во-вторых, пока модули никак не интегрированы с существующими подходами к дистрибьюции приложений — maven and gradle. Кроме того, совсем не ясно, какую проблему решают Модули для пользовательского кода, вне JDK.

Поэтому хочется обсудить вещи, пришедшие к нам с Девяткой, кроме Модулей. К сожалению, тут не так много всего вкусного и сахарного. Причём, многие вещи были выкинуты из Java 9, хотя должны были быть там (например, встроенный http client).

Хорошая статья про Сахар Девятки — https://habrahabr.ru/company/jugru/blog/336864/ В целом, там описаны все основные вещи, о которых нам стоит знать.

Продублирую самые интересные вещи для меня, которые бы я уже сейчас не прочь использовать. Хотя, надо признать, весь новый сахар давно был в Google Guava, который есть, наверное, в любом проекте. Поэтому, с точки зрения личного продуктивити, вряд ли что-то изменится в нашей жизни.

Билдеры для коллекций

Ура, теперь мы можем создавать Списки, Мапы и Сеты буквально в одну строку. И для этого нам не потребуется каких-либо либ. Выглядит всё это вот так вот:

List<String> strings = List.of("1", "2", "3");

Map<Integer, Integer> integerIntegerMap = Map.of(1, 2);

Set<Integer> integers = Set.of(1, 2, 3);

Важно, что все эти коллекции — Незменяемы. Ничего добавлять или удалять из них нельзя — схватите UnsupportedOperationException.

Сахар для Optional

В классе java.util.Optional появилось 3 новых метода, которые драмматически улучшат вашу жизнь.

Первый — ifPresentOrElse.

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
    if (value != null) {
        action.accept(value);
    } else {
        emptyAction.run();
    }
}

Ничего хитрого. Раньше был просто метод ifPresent, который принимал Consumer, который применялся у хранящемосю в Опшенале значению. Теперь появилась возможность задать fallback.

Optional.empty().ifPresentOrElse(null, () -> System.out.println("fallback"));

Второй новый метод — or.

public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
    Objects.requireNonNull(supplier);
    if (isPresent()) {
        return this;
    } else {
        @SuppressWarnings("unchecked")
        Optional<T> r = (Optional<T>) supplier.get();
        return Objects.requireNonNull(r);
    }
}

Тут тоже ничего хитрого. Либо мы возращаем наш существующий Опшенал, либо, если он отсутствует, продьюсируем новый и возвращаем его.

Optional.empty()
        .or(Optional::empty)
        .or(() -> Optional.of("the real value"))
        .ifPresent(System.out::println);

Третий новый метод — stream.

public Stream<T> stream() {
    if (!isPresent()) {
        return Stream.empty();
    } else {
        return Stream.of(value);
    }
}

Этот метод можно использовать, как пишут создатели языка, для того, чтобы из Стрима Опешанолов, получить Стрим реально представленных значений.

Stream<Optional<T>> os = ..
Stream<T> s = os.flatMap(Optional::stream)

В общем, перед нами вполне полезные методы для работы с Монадой Мейби. Всё это можно было сделать и раньше, но теперь — это стало удобнее.

HTTP/2 Client

Другое, о чём бы я хотел поговорить в свете Девятки — это новый, кленовый HTTP-клиент. По суте, полная копия Apache HttpClient. Кажется, что перед нами действительно довольно гибкий клиент, позволяющий настроить буквально всё. Http 1.1, Http 2, авторизация, куки, синрохронность и асинхронность, настройка пула потоков (экзекьютор), хэдэра — да, всё это должно было быть в HTTP клиенте джавы.

К сожалению, Клиент был в последний момент вынесен из JDK 9 в инкубатор — место, в котором теперь разрабатываются все новые фичи Джавы. Однако есть и хорошие новости — нам обещают завести клиент в Десятке.

Для того чтобы уже сейчас, в Java 9, попробовать клиент, требуется сделать несколько вещей. Во-первых, вы должны подключить в зависимости своего модуля — модуль, в котором как раз таки и реализован клиент. Для этого в файле module-info.java, нужно написать что-то типа того:

module ru.hixon {
    requires jdk.incubator.httpclient;
}

Где вместо ru.hixon — вы должны задать имя пакета, в котором хотите использовать Клиент из Инкубатора. После этого вы можете использовать все новые классы для Клиента.

Самый простой GET запрос можно сделать примерно так:

package ru.hixon;

import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;


public class Main {

    public static void main(String[] args)
            throws IOException, InterruptedException, URISyntaxException {

        HttpClient httpClient = HttpClient.newHttpClient();
        
        HttpRequest httpRequest = HttpRequest.newBuilder(new URI("http://hixon.ru"))
                .GET()
                .build();

        HttpResponse response = httpClient.send(httpRequest,
                HttpResponse.BodyHandler.asString(Charset.forName("UTF-8")));

        System.out.println(response.body());
    }
}


Асинхронный вариант этого куда такой же простой:

HttpClient httpClient = HttpClient.newHttpClient();

HttpRequest httpRequest = HttpRequest.newBuilder(new URI("http://hixon.ru"))
        .GET()
        .build();

CompletableFuture<HttpResponse<String>> responseFuture = httpClient.sendAsync(httpRequest,
        HttpResponse.BodyHandler.asString(Charset.forName("UTF-8")));

HttpResponse<String> response = responseFuture.get(10, TimeUnit.SECONDS);

System.out.println(response.body());

В Общем, перед нами вполне удобный HTTP-клиент. Пока он не продакшен-реди — чего только стоит закомментированный код в исходниках клиента в релизной JDK).

///**
// * Returns a used {@code ByteBuffer} to this request processor. When the
// * HTTP implementation has finished sending the contents of a buffer,
// * this method is called to return it to the processor for re-use.
// *
// * @param buffer a used ByteBuffer
// */
//void returnBuffer(ByteBuffer buffer);

Будем надеяться, что уже скоро мы получим вполне годную вещь. А сейчас продолжим использовать клиенты Апача, Спринга и иже с ними.

Другие изменения Java 9

Как я выше уже сказал, на хабре есть прекрасная статья, которая описывает всё это. Но ещё раз однословно расскажу, что появилось нового — мало кто из вас забанен на хабре.

Улучение Стримов — доработана работа с Optional. Добавлены популярные ранее в других языках программирования конструкции takeWhile и dropWhile, которые позволяют не обрабатывать часть стрима по некоторому условию.

Улучшение CompletableFuture. Добавлен метод copy, который позволяет полностью скопировать Фьючу. Самое главное — завершение скопированной фьючи никак не отражается на прородители. Ещё важно, что завершение Фьючи-родителя завершит автоматически всех детей, даже скопированных.

Появился API для работы с процессами операционной системы — класс ProcessHandle. Теперь можно легко, а самое главное — кросплатформерно — работать с сущностью Процесса, получая всю нужную информацию про него.

Очень важная вещь — появление класса StackWalker, который позволяет работать со стектрейсами, не создавая объектов Exception. Я думаю, что все понимают, почему это важно. Создание объектов исключения — это дорогая операция. Таким образом, туллинг, которому приходится анализировать стэктрейсы, теперь может здорово ускорится, если его переписать с использованием нового API.

Супер противоречивая вещь — появилась возможность реализовывать приватные методы в интерфейсах. Думаю, все знают Парадокс разбитых окон. Перед нами — отличный пример этого парадокса. Стоило в Java 8 придумать костыль, херовое архитектурное решение (с дефолтными методами), как в Java 9 мы уже вынуждены придумывать ещё более херовое решение, которое нужно нам для поддержки кодобазы.

interface AlmostClass {
    default Integer getNumber() {
        return getMagicNumber();
    }

    private Integer getMagicNumber() {
        return 42;
    }
}

Подводя итоги. Девятка — не такая плохая, чтобы на неё не переходить. Однако пока совсем не понятно, что будет в ситуации с моделями, старым кодом, Unsafe и вот этим всем. Поэтому, можно предположить, что скорость адоптации новой версии Джавы будет, к сожалению, меньше чем в восьмёрке. Да и sales points у данного релиза — нууу, такой себе.

Категории: Программирование