В эту среду, 30 августа, в офисе компании Oracle состоялась встреча JUG.ru с Олегом Шелаевым, Developer Advocate в компании ZeroTurnaround, которая делает JRebel и XRebel. Тема встречи — инструменты создания многопоточных программ на Java (от конструирования велосипедов и запуска потоков ручками, до ForkJoinPool-ов, зеленых потоков и транзакционной памяти).
Конечно, мы поинтересовались, какие фишки Java 9 считают в ZeroTurnaround наиболее полезными. В результате, разжились статьей, которую вы сейчас читаете. Оригинал статьи Олег опубликовал в блоге RebelLabs, там есть еще много интересного.
Итак, начали.
Мы долго ждали Java 9, и вот, релиз уже не за горами. Ура! Это был не самый простой путь, но тем не менее, даже самые жаркие споры о системе модулей потихоньку продвигаются вперед, и большинство участников приходят к согласию. Скорей всего, релиз состоится совсем скоро, например, 21 сентября 2017 года.
В этом посте я не буду останавливаться на детальном обсуждении системы модулей. Вместо этого, мы поговорим о вещах, которые сможет использовать любой разработчик: о нововведениях в языке Java и стандартных API.
Дальше будет список наших любимых новшеств в Java 9 API. В принципе, чтобы понять суть, достаточно взглянуть на примеры кода. Но можно запустить JShell и самостоятельно все эти примеры, своими глазами взглянуть на результаты.
Запускай JShell, я подожду… готов? Нет еще? Окей… сделал? Все еще нет? Да, на разгорев нужно время… ок, запустилось, отлично! Начинаем.
Фабричные методы для коллекций
Одна из наиболее ожидаемых фишек Java 9 — возможность делать литералы коллекций, чтобы удобней записывать самые простые случаи. Шутка, конечно, мы же говорим о Java, литералов у нас нет, есть только статические фабричные методы. Тем не менее, теперь мы можем легко создавать List
, Map
и Set
, используя готовые методы:
jshell> List.of(1, 2, 3)
$1 ==> [1, 2, 3]
jshell> Set.of(1, 2)
$2 ==> [2, 1]
jshell> Map.of("hello", "world")
$3 ==> {hello=world}
Это стало возможным благодаря появлению статических методов в интерфейсах (Java 8), использовать которые научились List
, Map
и Set
.
В результате, создается немутабельная коллекция, оптимизированная для работы с максимальной производительностью. Например, List1
хранит значение в поле класса, что ускоряет доступ до него.
jshell> List.of(1).getClass()
$4 ==> class java.util.ImmutableCollections$List1
Стримы
В Stream API добавили парочку очень полезных возможностей. В частности, методы dropWhile
и takeWhile
. Как можно предположить по названию, dropWhile
выбрасывает элементы с начала и до тех пор, пока не будет выполнено условие, а takeWhile
— забирает элементы вплоть до выполнения условия.
jshell> IntStream.range(1, 10).dropWhile(x -> x < 5).forEach(System.out::println)
5
6
7
8
9
Следующее полезное дополнение — метод iterate()
. Он позволяет заменять циклы стримами. Нужно передать ему изначальные значение стрима, условие (когда нужно остановить итерации), и функцию перехода (как именно будет получаться следующий элемент).
jshell> IntStream.iterate(0, x -> x < 3, x -> x + 1).forEach(System.out::println)
0
1
2
Если вам вдруг хотелось делать вычисления с фиксированной точкой на стримах, эта мечта может исполниться в Java 9.
Optional
Если вдруг кто не помнит, как их использовать, у нас есть отличная шпаргалка. В Java 9 наконец-то добавили метод or()
, позволяющий в одну строчку связывать разные Optional’ы, не опускаясь до постоянных проверок на isPresent()
.
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
jshell> Optional.empty().or(() -> Optional.of("RebelLabs"))
$5 ==> Optional[RebelLabs]
Следующее хорошее дополнение — возможность преобразовывать Optional в стрим, содержащий не более одного элемента. Это реально полезно, если хочется использовать ленивые стримы. В примере ниже можно увидеть разницу своими глазами. Если вызвать map()
на Optional, маппинг произойдет мгновенно, а вот на стриме — нет.
jshell> Optional.of(1).map(x -> x * 3)
$10 ==> Optional[3]
jshell> Optional.of(1).stream().map(x -> x * 3)
$11 ==> java.util.stream.ReferencePipeline$3@67b92f0a
И наконец, у нас появился метод ifPresentOrElse
. В Java 8, можно было определить поведение только для случая, когда значение Optional существует. В Java 9 стало возможно указать два разных Runnable, определяющих что делать, если значение существует, и если не существует.
jshell> Optional.empty().ifPresentOrElse(x -> System.out.println(x), () -> System.out.println("empty"));
empty
Completable future
Еще одним кусочком API, который хорошенько заполировали, оказался класс CompletableFuture
. В него добавили парочку отличных вещей, позволяющих писать еще более корректный многопоточный код.
Один из самых крутых методов — это copy()
, который возвращает немутабельную копию этого CompletableFuture
. В следующем примере, мы создаем CompletableFuture
, делаем его копию и проверяем, что завершение копии не влияет на исходный объект. Это очень-очень полезно, когда создается асинхронный API, возвращающий CompletableFuture
. Раньше нужно было знатно помучиться, обруливая ситуации, когда клиент может сам завершить CompletableFuture
, возвращенное из такого API. Сейчас достаточно просто взывать метод copy()
.
jshell> CompletableFuture<String> future = new CompletableFuture<>()
future ==> java.util.concurrent.CompletableFuture@35d176f7[Not completed]
jshell> future.copy()
$15 ==> java.util.concurrent.CompletableFuture@4973813a[Not completed]
jshell> future.isDone()
$17 ==> false
jshell> $15.isDone()
$18 ==> false
jshell> $15.complete("JRebel")
$19 ==> true
jshell> $15.isDone()
$20 ==> true
jshell> future.isDone()
$21 ==> false
Но самое крутое в том, что остановка родителя распространяется на все копии!
jshell> CompletableFuture<String> future = new CompletableFuture<>()
future ==> java.util.concurrent.CompletableFuture@4bbfb90a[Not completed]
jshell> future.copy()
$24 ==> java.util.concurrent.CompletableFuture@5a8806ef[Not completed]
jshell> future.complete("XRebel")
$25 ==> true
jshell> $24.isDone()
$26 ==> true
Кроме того, наконец-то в них добавили таймауты. Работа с асинхронным API, без наличия встроенных функций работы с таймаутами, была весьма напряжной. В Java 9 стало возможным точно определять способ завершения CompletableFuture
, после истечения вручную заданного промежутка времени.
jshell> CompletableFuture<String> future = new CompletableFuture<>()
future ==> java.util.concurrent.CompletableFuture@67205a84[Not completed]
jshell> future.completeOnTimeout("Isn't this amazing", 1, TimeUnit.SECONDS)
$28 ==> java.util.concurrent.CompletableFuture@67205a84[Not completed, 1 dependents]
jshell> future.isDone()
$29 ==> true
API управления процессами
До Java 9, управление процессами было совершено не таким кроссплатформенным, как хотелось бы верить. Работа с подпроцессами раньше была несколько кривой, и вот в Java 9 её наконец выпрямили. Java 9 добавляет класс ProcessHandle
, который предоставляет API для анализа текущего процесса, других процессов, найденных по пиду, их дочерних процессов, и так далее. Просто посмотрите на пример:
jshell> ProcessHandle current = ProcessHandle.current();
current ==> 6349
jshell> current.pid()
$33 ==> 6349
jshell> current.info().\TAB
arguments() command() commandLine() equals( getClass()
hashCode() notify() notifyAll() startInstant() toString()
totalCpuDuration() user() wait(
jshell> current.info().command()
$34 ==> Optional[/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/bin/java]
Скорей всего, самой часто используемой функцией будет получение командной строки и аргументов, использованных для запуска процесса, но остальные фичи тоже вполне достойны внимания.
Еще одна популярная задача, которую будет куда удобней делать в Java 9 — запуск кода сразу после того, как процесс завершился. Java 9 предлагает для этого воспользоваться новым методом:
CompletableFuture<Process> onExit()
Указываете, что же вы хотите сделать, и это просто работает. Нет больше слёзок и нестабильных чужих библиотек.
StackWalker
Возрадуйтесь, ненавистники исключений! Теперь можно работать со стектрейсами, не создавая объектов Exception
. Добро пожаловать в StackWalker
!
StackWalker
дает возможность бродить по стеку, фильтровать его, и эффективно делать разные другие вещи. Этот пример выдернет верхние 5 элементов из стектрейса:
jshell> StackWalker.getInstance().walk(s -> s.limit(5).collect(Collectors.toList()));
$36 ==> [do_it$(java:36), jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:209), jdk.jshell/jdk.jshell.execution.RemoteExecutionControl.invoke(RemoteExecutionControl.java:116), jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:119), jdk.jshell/jdk.jshell.execution.ExecutionControlForwarder.processCommand(ExecutionControlForwarder.java:134)]
Улучшения в языке Java
Улучшаются не только API, но и сам язык. Во-первых, символ _
(подчёркивание) больше не является корректным идентификатором. Если вы зачем-то его используете, придется перейти на двойное подчеркивание! (Подсказка: не делайте этого).
jshell> int _ = 1
| Error:
| as of release 9, '_' is a keyword, and may not be used as an identifier
| int _ = 1
| ^
| Error:
| reached end of file while parsing
| int _ = 1
| ^
jshell> int __ = 1;
__ ==> 1
Это для того, чтобы в будущем можно было заменять на подчеркивание ненужные (опциональные) параметры при вызове функции.
Интерфейсы тоже немного доработали. Интерфейсы в Java 9 смогут содержать приватные методы. В Java 8 мы получили возможность хранить некую общую логику в default-методах. Теперь мы сможем выделять общую логику и внутри интерфейсов, без необходимости создания вспомогательных классов.
Вот вам небольшой синтетический пример:
jshell> interface A { private int zero() { return 0;} default int one() { return zero() + 1;}}
| created interface A
Ну и наконец, последнее новшество. Теперь можно использовать effectively final переменные в блоках try-with-resources. Это упрощает код, не нужно больше объявлять переменные внутри try
. Просто работаете с ними в блоке try
, и это компилируется.
boolean a() throws Exception {
Socket s = new Socket();
try (s) { }
return s.isClosed();
}
После выполнения блока try
, все упомянутые там AutoClosable
закономерно закроются.
Заключение
Ура! Мы рассмотрели кучу всего, и это далеко не все новшества, появившиеся в Java 9. Тем не менее, перечисленные выше вещи кажутся нам наиболее полезными, и пользоваться ими будут при первой возможности.
Выпуск Java 9 готовится уже довольно давно, и всем нам пора хорошенько разобраться, как она на нас повлияет. Сейчас это уместно как никогда, учитывая, что режим работы с clasthpath останется неизменным, и переход на Java 9 может оказаться простым и безболезненным. Можно прямо сейчас скачать готовую сборку Java 9, разобраться с новыми API, распробовать их, и приготовиться к наступающему светлому будущему!
Авторы
Олег Шелаев — Java-разработчик и Developer Advocate в ZeroTurnaround. Когда не занимается написанием java agent-ов или тестов, пишет в блог RebelLabs или выступает на конференциях. В свободное время пытается продвигать науку в Тартуском университете, изучая проблемы динамических обновлений программ.
Комментарии (77)
Tyrr
31.08.2017 16:22+3Хорошая статья! на некоторых моментах прям выдохнул) но выбор между интерфейсом и абстрактным классом теперь кажется сложнее, есть ли однозначный ответ, когда что выбирать?
ferasinka
31.08.2017 16:27+1У интерфейса нет и не может быть состояния.
olegchir Автор
31.08.2017 16:33+7public interface Отрывать { AtomicInteger руки = new AtomicInteger(10); private static void заТакое() { руки.set(11); } }
Beholder
31.08.2017 17:25+2Ну и это поле будет public static final, то есть состояния у интерфейса всё равно нет.
igorperciuleac
31.08.2017 18:29оно ведь mutable
Beholder
31.08.2017 18:32+3Ну состояние будет одно на весь classloader, а не на каждый объект, который реализует этот интерфейс.
gamerforEA
01.09.2017 18:07+4public interface StateInterface { Map<StateInterface, Integer> states = new IdentityHashMap<>(); default int getState() { return states.getOrDefault(this, 0); } default void setState(int state) { states.put(this, state); } }
AstarothAst
31.08.2017 17:02+1CompletableFuture<Process> onExit()
Я правильно понимаю, что с помощью этой штуки можно сделать процесс-маклауд? :)izzholtik
31.08.2017 17:33Множественное наследование всё ближе? >:D
А если серьёзно, то какого чёрта класс интерфейса содержит метод, возвращающий какую-то «избранную» реализацию?
— не туда написал. Интерфейс у хабра всё ещё не тотр.olegchir Автор
04.09.2017 13:00+1в таких случаях принято комментарий целиком заменять на слово «del», и потом писать в правильное место. Их обычно не минусуют, потому что всем и так ясно, откуда пошла такая фигня
olegchir Автор
31.08.2017 17:45+5Борис Бритва, или Борис-Хрен-Попадешь, резкий, как удар серпом по яйцам, и жесткий, как удар молотом. Живой советский герб. Говорят, эту сволочь вообще невозможно убить.
www.youtube.com/watch?v=n7itx7O3b_k
Beholder
31.08.2017 17:33-2Половина или больше этих фич уже есть в Kotlin.
Вот почему-то ничего не пишут, закрыли ли они кучу багов и реквестов по JavaFx.Optik
01.09.2017 07:11+7При чем здесь вообще котлин? Все его фичи тоже уже были в других языках.
werewolfspb
01.09.2017 14:00-3При том, что, выходит, изменения в языке / stdlib уже почти не нужны при 100% Java <-> Kotlin interop — если кому нужен сахар, но возьмёт Котлин и будет использовать его. Или любой другой JVM-based язык и компилятор.
Можно было бы сосредоточиться на платформе и hardcore фичах, типа modularity, а не на языке. Тогда бы и апдейты, глядишь, быстрее выходили.
Optik
01.09.2017 14:44+1В одном из предыдущих
срачейтопиков путем докапывания до котлин-адепта выяснилось, что интероп все же совсем не 100%. Также как и у других jvm языков.
Тем кто пишет core-либы смириться и жить на старых возможностях языка? Стоит забить на тех, кому нравится java?werewolfspb
01.09.2017 15:15-1Пришлите ссылку на топик, пожалуйста, если не лень, я бы с удовольствием почитал. Есть, конечно, нюансы, но они достаточно в крайних случаях мешают жить.
Нет, на тех, кому нравится Java, не забить, претензии только к срокам выхода обновлений. Так-то это всё полезные безусловно вещи. Надеюсь, Гёц сотоварищи таки будут продвигать апдейты пошустрее.
Просто те, кому нравится ТОЛЬКО Java, должны понимать, что они вот с такой скоростью будут получать обновления и плюшки. Это, кроме всего прочего, означает, что молодёжь будет изучать другие языки тоже и с бОльшим удовольствием придёт туда, где пишут не только на чистой Java.dzugaru
02.09.2017 00:29+2>Это, кроме всего прочего, означает, что молодёжь будет изучать другие языки тоже и с бОльшим удовольствием придёт туда, где пишут не только на чистой Java.
Слава богу, хипстеров нам в джаве только еще и не хватало. Пусть на джаваскрипте пишут.
guai
01.09.2017 14:44+3100% Java <-> Kotlin
cool story, browerewolfspb
01.09.2017 15:16-2:) Киньте ссылок на доказательства обратного, пожалуйста, если есть :)
guai
01.09.2017 17:53ссылок нет. опыт есть. рекурсивные дженерики с котлином не пробовали поженить?
да и не рекурсивные, бывает, глючат. Comparable с дженериком ему чота не нравится.
«Добавьте мне override». Добавил. «Я не понимаю, зачем тут override» :)
Приходилось делать странные финты ушами, типа сделать интерфейс на котлине, от него занаследовать явавский класс, от него опять котлин, чтоб только удовлетворить компилятор.werewolfspb
01.09.2017 18:09Понятно, спасибо. Пока не пробовал с generics, попробую. Это какая версия была у вас?
vlanko
01.09.2017 15:44+2фигню говорите.
Оракл хотел ввести модульность. Но им не далиговнокодеры, не желающие переписывать свои программы. //Red Hat и IBMolegchir Автор
04.09.2017 12:57Имхо там слегка сложней.
Есть договоренность, что если спека всеми принята, то ее делают.
А тут получилось, что люди прямо перед выпуском напрямую отказались от своих слов, посчитав их глупостью.
И дело там не только в «переписывать программы», а в том, что изначальную спеку нужно делать куда масштабней и умней.
Обычная мелкая гражданская война, в которой все правы и неправы одновременно, а страдают вообще другие люди :)
DigitalSmile
01.09.2017 09:26Вот почему-то ничего не пишут, закрыли ли они кучу багов и реквестов по JavaFx
По состоянию на 2016 год (если не читали):
static.rainfocus.com/oracle/oow16/sess/1462485593256001c2xn/ppt/JavaFX%209%20-%20New%20and%20Noteworthy.pdf
olegchir Автор
01.09.2017 19:08+2Java нужна потому, что это Java. Язык с невероятно долгой совместимостью и неформальными гарантиями что новые фичи будут хорошо работать очень долго (может быть, те же фичи будут использовать наши внуки), с хорошо подогнанными друг другу фичами (проходят годы обсуждений, прежде чем какая-то фича попадает на публику), с поддержкой Oracle, ну и в конце концов — это язык, на котором говорит большинство разработчиков в мире. Котлин — хороший язык, но он не об этом. Тем, кому нужна Java, нужна именно Java.
Crandel
31.08.2017 17:59-> IntStream.range(1, 10).dropWhile(x -> x < 5).forEach(System.out::println) | Error: | cannot find symbol | symbol: variable IntStream | IntStream.range(1, 10).dropWhile(x -> x < 5).forEach(System.out::println) | ^-------^
Ubuntu 16.04
jshell 9-internal java -version java version "9" Java(TM) SE Runtime Environment (build 9+181) Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)
k0ldbl00d
31.08.2017 18:12-1JRE станет еще более параноидальным?
Lure_of_Chaos
01.09.2017 09:02+1Если Вы о системе модулей, то ДА.
k0ldbl00d
01.09.2017 10:23-1Я о том, что я не Java-программист, а простой админ, и у меня есть горстка не нового, но вполне работоспособного железа, которое управляется через Java-апплеты, и с каждым мажорным релизом их всё сложнее запустить, потому что обновлений к этим железкам уже никто не выпустит, а параноики из Oracle не дают возможности отключить проверки, которые мешают запускать апплеты, и вообще мне не нужны.
Lure_of_Chaos
01.09.2017 10:35+2Врядли апплеты можно закопать еще глубже, чем их уже закопали, так что нововведения уже совсем не о том.
(хотя я лично скорблю — хороня флеш уже сколько баянов порвали, а такие вкусные апплеты, имхо с меньшими проблемами безопасности, убили прямо в расцвете их сил)
Но также я наблюдаю развитие языков программирования как платформ. Java, увы, полностью исчезла из интернета, ради которого создавалась, и ушла в глухой сервер-энтерпрайз и мобайл(андроид).
snuk182
31.08.2017 18:31+4jshell> Map.of("hello", "world") $3 ==> {hello=world}
Полна боли и страдания жизнь без кортежей и/или классов данных.olegchir Автор
31.08.2017 18:52+3Да, поэтому:
1) в конпеляторе есть исследование литералов: http://openjdk.java.net/jeps/186 (это именно исследование, а не финальная спека). Она прям напрямую связана, с обсуждаемой здесь фичей convenience methods (http://openjdk.java.net/jeps/269). Фичу курирует Гёц.
2) уже есть проротипы type values, я писал о них недавно, и буду еще. Фичу курирует Гёц же, и Роуз.
Короче, всё будет, но не мгновенно. Возможно, ускорение релизного цикла, о котором недавно писал Рейнхольт, позволит выкатить подобные mast-have фичи поперед других, более масштабных, изменений, и мы увидим их в ближайшем будущем.
Nakosika
31.08.2017 18:57Почему они просто в библиотеку не добавили методы Optional? Зачем ждать новой версии языка?
cypok
01.09.2017 06:56+1Это не новая версия языка, это новая версия платформы Java, куда входит и язык, и стандартная библиотека, и тулинг (
jshell
, например).
AndreyRubankov
05.09.2017 09:57К счастью, в Java мире нету апгрейдов стандартной библиотеке в отрыве от выпуска новой версии платформы.
Любое изменение SDK должно быть только в мажорных релизах, иначе будет хаос и про существование стабильности можно будет забыть.
И да, ускорение релизного цикла – это было бы очень круто!
Но с планами по развитию платформы, ускорение релизного цикла, это не такой простой шаг. Зарелизить те же Панаму или Валхаллу – это не так просто будет и скорее всего повлечет откладывания релизов на полгода-год.
AlexTheLost
01.09.2017 00:02+7Выглядит и звучит так же, как когда чиновники говорят о прорывах которые в других странах появились уже 10 лет назад.)
Что например мешало добавить удобные методы для создания и манипуляции коллекциями раньше, мне не понятно.
Lure_of_Chaos
01.09.2017 08:52+5приватные методы в интерфейсах
Ребята, пора уже честно признаться — вам очень не хваталомножественного наследованияпримесей, но вы стесняетесь вводить это бгмерзкое понятие и поэтому распидколбашиваете идеологию интерфейсов.
Интерфейсов, Карл! (запах горелого)
Lure_of_Chaos
01.09.2017 08:58кстати, поправьте меня, если я неправ, но запилить некоторые примочки Optional и .of() можно собственными руками уже в java8
fzn7
01.09.2017 09:10Раз пошла такая пьянка, предлагаю выпилить for
Lure_of_Chaos
01.09.2017 10:15Выпиливать — не наш метод.
А вот без Properties, как в C#, до сих пор грустно. Как хорошо, что есть Lombok, правда, IDE его все так же не понимают…olegchir Автор
01.09.2017 11:19Гёц когда-то об этом говорил. Проблема в том, что проперти можно реализовать по-разному, и никакая из реализаций не подойдет всем желающим (включая желающих совместимости для легаси двадцатилетней давности). А маркетинговый эффект этой фичи будет минимальный, по сравнению с приложенными усилиями (т.е. если запилить что-то важное типа лямбд — это скорей будет стимулом поменять $languagename на Java, а вот введение пропертей — не особо)
Nakosika
01.09.2017 13:37+2Зачем они? Только баги провоцируют. Читаешь код и хз что происходит пока все поля не прощелкаешь и не убедишься что это не функции на самом деле.
blaketsk
01.09.2017 12:59Это стало возможным благодаря появлению статических методов в интерфейсах (Java 8)
Дефолтных?olegchir Автор
01.09.2017 13:00public interface HelloWordable { public static void hello() { System.out.println("Hello World!"); } }
hazard2
01.09.2017 15:34+2С удивлением понял, что можно написать вот так:
public interface Main { public static void main(String[] args) { System.out.println("Hello World!"); } }
Или даже вот так:
@SpringBootApplication @Configuration public interface TestApplication { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(TestApplication.class, args); } }
elegorod
04.09.2017 12:27List.of(1, 2, 3)
Неожиданно, что это возвращает неизменяемый List. Типичная ситуация в моём коде — создать List или Map с парой элементов, а потом добавить туда ещё несколько. Через List.of это сфейлится в рантайме с Exception-ом. Думаю, многие проограммисты на этом обожгутся.
Как теперь лучше создавать изменяемые списки, вот так?
new ArrayList(List.of(1, 2, 3))
Лучше бы сделали ArrayList.of(1, 2, 3)olegchir Автор
04.09.2017 12:47+1Вопрос в том, что стратегический план должен корректировать не только саму форму API, но и способ того, как люди пользуются Java. Направлять мир в правильное русло. Это один из смыслов существования архитектурных советов и выпуска ими рекомендаций. Если бы нужен был просто вспомогательный метод, сделать его не проблема, или уже стащить готовый из Гуавы.
С точки зрения всеобщей схемы всего, верной практикой считается использование иммутабельных конструкций. Нужно всячески поощрять разработчиков к иммутабельности, а стиль программирования — к функциональной чистоте.
Если бы существовала мутабельная версия этой конструкции, все говнокодеры мира тут же начали бы ее использовать (не нужно учиться новым вещам, плодить мутабельное всегда проще), а всем остальным пришлось бы смириться с таким положением вещей по причине частоты распространения.
Имхо, именно поэтому, для создания мутабельной версии выбрана специально переусложненная форма. Именно такая как у тебя в примере — использование копирующего конструктора поверх коллекции, полученной через convenience method.
Я не чтобы оракул оракла, это чуть ли не открытым текстом сказано в JEP-269 Convenience Factories.
В дальнейшем эти идеи могут быть развиты с помощью JEP-186 Collection Literals. Предлагается не мусорить с помощью List.of, а сразу писать List list = #[ 1, 2, 3 ];. Но это совершенно отдельный (тоже стратегический) вопрос.
olegchir Автор
Друзья, внесу немного оффтопика сейчас, т.к. завтра будет поздно. Сегодня (31 августа) — последний день, когда на Joker 2017 всё еще можно купить билеты по вкусной летней стоимости. До конца дня остались считаные часы. Кто хотел определиться — определяйтесь сейчас.