На этой неделе, 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(); HttpResponseresponse = 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 у данного релиза — нууу, такой себе.
Категории: Программирование