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