Всем привет. Месяц назад вышла Java 11. Все круто.
В этом посте я бы хотел показать, чего еще очень не хватает рядовому Java-разработчику при каждодневной разработке. Это, скорее, просто мысли в слух о том, что хотелось бы увидеть в ближайших релизах, так как лично я с этими конструкциями работаю довольно часто. Поехали.
Pattern matching
Пожалуй, единственная конструкция из всего списка, которую я использую почти каждый день:
if (obj instanceof Device) { Device device = (Device) obj; ... }
Для нас, как разработчиков, вполне очевидно, что если мы сделали проверку на тип переменной и она прошла, то мы вполне можем оперировать переменной как объектом этого типа. Сейчас же мы постоянно должны приводить переменную к нужному типу явно. Уже даже появился JEP305для этой задачи, который предлагает следующий выход:
if (x matches Device device) { // can use device here }
Но, судя по всему, в ближайший год это не попадет в джаву. Остается только надеяться, что фичу не придется ждать 10 лет.
switch for class
Синтаксис не очень частый, но иногда просто бесит, что нельзя сделать простой:
switch (obj.getClass()) { case Integer.class:… case Long.class: ... }
Лично мне не понятно, почему такая простая, казалось бы, операция все еще не поддерживается джавой нативно. Конечно, эту проблему всегда можно обойти полиморфизмом или пачкой if else
или сделать class.getSimpleName()
и свитч по строке, но это не так удобно. Да и по производительности явно не лучший вариант.
List<Integer>
to int[]
Каждому разработчику известно чувство, когда он фокусируется, входит в поток и начинает эффективно колбасить. И вот все идет классно, пока не нужно сделать преобразование List<Integer>
в int[]
или наоборот. Нельзя просто так взять и преобразовать лист целых чисел в массив примитивов. Сейчас, чтобы это сделать, нужно или писать цикл преобразования:
int[] integerListToInt(List<Integer> integers) { int[] result = new int[integers.size()]; for (int i = 0; i < result.length; i++) { result[i] = integers.get(i); } return result; }
Ну или через стримы:
int[] array = list.stream().mapToInt(i->i).toArray();
Оба варианта, очевидно, не самые оптимальные и удобные. Да и вопрос на СО, которому уже 10 лет с сотнями голосов и сотнями тысяч просмотров как бы намекает нам про актуальность. С преобразованием в обратную сторону — такая же ситуация.
Да, можно обойтись без примитивов и конструкция будет выглядеть получше, но все еще не очень:
list.toArray(new Integer[0])
new ArrayList(array)
Если отсутствие преобразования List<Integer> -> int[]
еще как-то можно объяснить разными сущностями int
и Integer
, то отсутствие конструктора с массивом для ArrayList
вообще не укладывается в голове. И почему за 20 лет существования языка нужно использовать:
new ArrayList<>(Arrays.asList(array))
вместо:
new ArrayList<>(array)
для мутабельного листа, до сих пор не понимаю. Да, легаси, все дела, но язык-то должен развиваться и становиться удобным. Очередной вопрос на СОс миллионом просмотров.
ConcurrentSet
Да, его до сих пор нету в джаве, и судя по всему никогда и не будет. К счастью, начиная с
ConcurrentHashMap.newKeySet()
Но, во-первых, про этот метод нужно знать, во-вторых, опять же вопрос удобства, ну и в-третьих, это не ConcurrentSet
, а просто Set
. А сам класс — некий KeySetView
. Почти в любом большом опенсорс-проекте, который я видел, был свой ConcurrentSet
.
Path API
Как только появилось новое Path API, я сразу на него перешел. И вот, 7 лет спустя, до сих пор каждый раз, когда мне нужно использовать это API — страдаю от этого. Вы не можете сделать new Path()
, так как это интерфейс. А статических фабричных методов в классе Paths
целых 2. И поэтому часто код с Path выглядит так:
Paths.get(dataDir.toString(), folder);
вместо:
Paths.get(dataDir, folder);
Это, конечно, не так критично, так как у Path
есть метод resolve()
, но в виду его специфики он не всегда подходит.
Вывод
Да, джава все еще далека от совершенства. Но последние релизы дают надежду, что в ближайшие несколько лет это изменится в лучшую сторону и язык обретет новую жизнь.
А что вам не нравится в сегодняшней Java?