instanceof
.Сегодня я хотел бы поиграться с этим новым оператором и рассмотреть особенности его работы более детально. Так как паттерн-матчинг по типу ещё не вошёл в главный репозиторий JDK, мне пришлось скачать репозиторий проекта Amber, в котором ведётся разработка новых синтаксических конструкций Java, и собрать JDK из этого репозитория.
Итак, первое, что мы сделаем — проверим версию Java, чтобы убедиться, что мы действительно используем JDK 14:
> java -version
openjdk version "14-internal" 2020-03-17
OpenJDK Runtime Environment (build 14-internal+0-adhoc.osboxes.amber-amber)
OpenJDK 64-Bit Server VM (build 14-internal+0-adhoc.osboxes.amber-amber, mixed mode, sharing)
Всё верно.
Теперь напишем небольшой кусок кода со «старым» оператором
instanceof
и запустим его:public class A {
public static void main(String[] args) {
new A().f("Hello, world!");
}
public void f(Object obj) {
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str.toLowerCase());
}
}
}
> java A.java
hello, world!
Работает. Это стандартная проверка на тип с последующим приведением. Подобные конструкции мы пишем изо дня в день, какую бы версию Java мы бы не использовали, хоть 1.0, хоть 13.
Но теперь у нас в руках Java 14, и давайте перепишем код с использованием улучшенного оператора
instanceof
(повторяющиеся строки кода в дальнейшем буду опускать):if (obj instanceof String str) {
System.out.println(str.toLowerCase());
}
> java --enable-preview --source 14 A.java
hello, world!
Прекрасно. Код стал чище, короче, безопаснее и читабельнее. Было три повторения слова String, стало одно. Заметьте, что мы не забыли указать аргументы
--enable-preview --source 14
, т.к. новый оператор является preview feature. Кроме того, внимательный читатель, наверное, заметил, что мы запустили исходный файл A.java напрямую, без компиляции. Такая возможность появилась в Java 11.Давайте попробуем написать что-нибудь более навороченное и добавим второе условие, которое использует только что объявленную переменную:
if (obj instanceof String str && str.length() > 5) {
System.out.println(str.toLowerCase());
}
Компилируется и работает. А что если поменять условия местами?
if (str.length() > 5 && obj instanceof String str) {
System.out.println(str.toLowerCase());
}
A.java:7: error: cannot find symbol
if (str.length() > 5 && obj instanceof String str) {
^
Ошибка компиляции. Чего и следовало ожидать: переменная
str
ещё не объявлена, а значит не может быть использована.Кстати, что с мутабельностью? Переменная final или нет? Пробуем:
if (obj instanceof String str) {
str = "World, hello!";
System.out.println(str.toLowerCase());
}
A.java:8: error: pattern binding str may not be assigned
str = "World, hello!";
^
Ага, переменная final. Это значит, что слово «переменная» здесь вообще не совсем корректно. Да и компилятор использует специальный термин «pattern binding». Поэтому предлагаю отныне говорить не «переменная», а «биндинг паттерна» (к сожалению, слово «binding» не очень хорошо переводится на русский).
С мутабельностью и терминологией разобрались. Поехали экспериментировать дальше. Вдруг у нас получится «сломать» компилятор?
Что если назвать переменную и биндинг паттерна одним и тем же именем?
if (obj instanceof String obj) {
System.out.println(obj.toLowerCase());
}
A.java:7: error: variable obj is already defined in method f(Object)
if (obj instanceof String obj) {
^
Логично. Перекрытие переменной из внешней области видимости не работает. Это эквивалентно тому, как если бы мы просто завели переменную
obj
второй раз в той же области видимости.А если так:
if (obj instanceof String str && obj instanceof String str) {
System.out.println(str.toLowerCase());
}
A.java:7: error: illegal attempt to redefine an existing match binding
if (obj instanceof String str && obj instanceof String str) {
^
Компилятор надёжен как бетон.
Что ещё можно попробовать? Давайте поиграемся с областями видимости. Если в ветке
if
определён биндинг, то будет ли он определён в ветке else
, если инвертировать условие?if (!(obj instanceof String str)) {
System.out.println("not a string");
} else {
System.out.println(str.toLowerCase());
}
Сработало. Компилятор не только надёжен, но ещё и умён.
А если так?
if (obj instanceof String str && true) {
System.out.println(str.toLowerCase());
}
Опять сработало. Компилятор корректно понимает, что условие сводится к простому
obj instanceof String str
.Неужели не удастся «сломать» компилятор?
Может, так?
if (obj instanceof String str || false) {
System.out.println(str.toLowerCase());
}
A.java:8: error: cannot find symbol
System.out.println(str.toLowerCase());
^
Ага! Вот это уже похоже на баг. Ведь все три условия абсолютно эквивалентны:
obj instanceof String str
obj instanceof String str && true
obj instanceof String str || false
С другой стороны, правила flow scoping довольно нетривиальны, и возможно такой случай действительно не должен работать. Но если смотреть чисто с человеческой точки зрения, то я считаю, что это баг.
Но да ладно, давайте попробуем ещё что-нибудь. Будет ли работать такое:
if (!(obj instanceof String str)) {
throw new RuntimeException();
}
System.out.println(str.toLowerCase());
Скомпилировалось. Это хорошо, поскольку этот код эквивалентен следующему:
if (!(obj instanceof String str)) {
throw new RuntimeException();
} else {
System.out.println(str.toLowerCase());
}
А так как оба варианта эквивалентны, то и программист ожидает, что они будут работать одинаково.
Что насчёт перекрытия полей?
public class A {
private String str;
public void f(Object obj) {
if (obj instanceof String str) {
System.out.println(str.toLowerCase());
} else {
System.out.println(str.toLowerCase());
}
}
}
Компилятор не заругался. Это вполне логично, потому что локальные переменные всегда могли перекрывать поля. Для биндингов паттернов, видимо, тоже решили не делать исключения. С другой стороны, такой код довольно хрупок. Одно неосторожное движение, и вы можете не заметить, как ваша ветка
if
сломалась:private boolean isOK() {
return false;
}
public void f(Object obj) {
if (obj instanceof String str || isOK()) {
System.out.println(str.toLowerCase());
} else {
System.out.println(str.toLowerCase());
}
}
В обеих ветвях теперь используется поле
str
, чего может не ожидать невнимательный программист. Чтобы как можно раньше обнаруживать подобные ошибки, используйте инспекции в IDE и разную подсветку синтаксиса для полей и переменных. А ещё я рекомендую всегда использовать квалификатор this
для полей. Это добавит ещё больше надёжности.Что ещё интересного? Как и «старый»
instanceof
, новый никогда не матчит null
. Это значит, что можно всегда полагаться на то, что биндинги паттернов никогда не могут быть null
:if (obj instanceof String str) {
System.out.println(str.toLowerCase()); // Никогда не выбросит NullPointerException
}
Кстати, используя это свойство, можно укоротить подобные цепочки:
if (a != null) {
B b = a.getB();
if (b != null) {
C c = b.getC();
if (c != null) {
System.out.println(c.getSize());
}
}
}
Если использовать
instanceof
, то код выше можно переписать так:if (a != null && a.getB() instanceof B b && b.getC() instanceof C c) {
System.out.println(c.getSize());
}
Напишите в комментариях, что вы думаете по поводу такого стиля. Стали ли бы вы использовать такую идиому?
Что насчёт дженериков?
import java.util.List;
public class A {
public static void main(String[] args) {
new A().f(List.of(1, 2, 3));
}
public void f(Object obj) {
if (obj instanceof List<Integer> list) {
System.out.println(list.size());
}
}
}
> java --enable-preview --source 14 A.java
Note: A.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
3
Очень интересно. Если «старый»
instanceof
поддерживает только instanceof List
или instanceof List<?>
, то новый работает с любым конкретным типом. Ждём первого человека, который попадётся вот в такую ловушку:if (obj instanceof List<Integer> list) {
System.out.println("Int list of size " + list.size());
} else if (obj instanceof List<String> list) {
System.out.println("String list of size " + list.size());
}
ИМХО, это довольно серьёзная проблема. С другой стороны, я не знаю, как можно было бы её исправить. Похоже, опять придётся полагаться на инспекции в IDE.
Выводы
В целом, новый паттерн-матчинг по типу работает очень круто. Улучшенный оператор
instanceof
позволяет делать не только тест на тип, но ещё и объявлять готовый биндинг этого типа, избавляя от необходимости ручного приведения. Это означает, что в коде будет меньше шума, и читателю будет гораздо проще разглядеть полезную логику. Например, большинство реализаций equals()
можно будет писать в одну строчку:public class Point {
private final int x, y;
…
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Point p && p.x == this.x && p.y == this.y;
}
}
С другой стороны, вызывают небольшие вопросы несколько спорных моментов:
- Не полностью прозрачные правила области видимости (пример с
instanceof || false
). - Перекрытие полей.
instanceof
и дженерики.
Однако это скорее мелкие придирки, нежели серьёзные претензии. В целом, огромные преимущества нового оператора
instanceof
определённо стоят его добавления язык. А если он ещё выйдет из состояния preview и станет стабильной синтаксической конструкцией, то это будет большой мотивацией наконец-то уйти с Java 8 на новую версию Java.P.S. У меня есть канал в Telegram, где я пишу о новостях Java. Призываю вас на него подписаться.
Комментарии (66)
xdenser
27.11.2019 11:03Не понятно это привязка или все же присваивание? Не рассмотрен случай, когда obj изменился после instanceof.
bugy
27.11.2019 11:10Хм, непонятно, почему решили сделать с объявлением дополнительной переменной
(myVar instanceof String str)
В котлине, насколько я знаю, можно просто с изначальной переменной дальше работать, будто она этого типа
В JEP про это написано: мы не хотим ad hoc а хотим pattern matching. Но имхо это странно — усложнять имеющийся синтакс ради потенциальной новой фичи, которая может быть ещё совершенно иначе будет реализована
Да ещё и на ровном месте добавлять смесь проблем с перекрытием переменных и умных условийigormich88
27.11.2019 11:31Насколько я понимаю подход описанный в статье один в один скопирован из C#.
PS: мне тоже вариант из Котлина кажется удобнее.Deosis
27.11.2019 12:17В C# это объяснили тем, что вместо myVar можно подставить любое выражение, и оно будет вычислено один раз.
mayorovp
27.11.2019 11:40Потому что введение в старый язык смарткастов может изменить работу существующего кода.
Например, вот в этом случае:
if (foo instanceof Bar) { foo.new Baz().run(); }
Если в классе Bar переопределен внутренний класс Baz — поведение кода после введения смарткастов изменится.
Кроме внутреннего класса, можно неудачно переопределить поле.
orionll Автор
27.11.2019 12:17Лично мне не нравятся смарткасты, потому что они могут работать только с final полями и final/effectively final переменными:
private Object field; ... if (this.field instanceof String) { // Можем использовать this.field как String здесь или нет? }
В Java-подходе такой проблемы нет:
if (this.field instanceof String str) { // Используем str }
bugy
27.11.2019 13:23Интересный пример, но "такой проблемы нет" только в случае атомарности оператора?
Я, к сожалению, не нашел ничего про concurrency в pattern matchingmayorovp
27.11.2019 13:38Тут речь идёт не о многопотоке, а о реентерабельности и внезапных связях.
Вот вам пример к чему это приводит в Typescript:
switch (derivation.dependenciesState) { // ... case IDerivationState.POSSIBLY_STALE: { // ... obj.get(); // ... // https://github.com/mobxjs/mobx/blob/master/src/core/derivation.ts#L117 if ((derivation.dependenciesState as any) === IDerivationState.STALE) { // ... } } }
Зачем тут каст к
any
? А чтобы заткнуть компилятор, который уверен, чтоderivation.dependenciesState
никак не может быть равенIDerivationState.STALE
внутри блокаcase IDerivationState.POSSIBLY_STALE
. Даже несмотря на то, чтоobj.get()
этот самыйdependenciesState
обязательно изменяет.
zagayevskiy
27.11.2019 11:18-2Компилятор надёжен как бетон.
Компилятор просто бетон /sarcasm
Джаву пора закапывать, кажется. Каждый релиз вызывает фейспалм.
Это называется smart cast и должно работать без всякой дополнительной фигни.mayorovp
27.11.2019 11:42Не должно. Обратная совместимость же! Вот если обратную совместимость нарушат — тогда джаву и правда будет пора закапывать...
zagayevskiy
27.11.2019 11:53А что «обратная совместимость»? Чем это лучше, чем введение нового оператора?
DieSlogan
28.11.2019 10:07У одной нашей команды есть куча проектов, которые работают на Java8, максимум. Совместимость уже частично нарушена.
Xobotun
28.11.2019 12:03В мире майнкрафта не получается запускать сервера с плагинами на версиях выше 8: почему-то код сервера, на который можно ставить плагины, требует какую-то дикую интроспекцию, которую Jigsaw запрещает. По крайней мере, мне это так объяснили. :)
tsypanov
27.11.2019 11:40+3Спасибо за хорошую статью, однозначно жирный плюс!
Смущает пара моментов:
1)
До этого всё в Java по умолчанию являлось non-final: поля, классы, методы, параметры методов и даже параметры лямбд.
Параметры лямбд действительно можно переприсваивать, только это не работает для переменных, захваченых лямбдами:
public void foo(String name) { String str = ""; cache.computeIfAbsent(name, beanName -> { str = beanName; // <--------- ошибка компиляции return beanName; }); }
2)
Вместо "биндинг" можно писать "связывание", ИМХО, это как раз тот момент, когда вместо заимствования лучше сделать дословный перевод.
3)
Ага! Вот это уже похоже на баг.
Писали ли вы в указанную в начале статьи рассылку? Что говорят разработчики?
Xobotun
27.11.2019 14:261) У лямбд, как мне кажется, всё же чуть иной случай: чтобы переменная могла быть захвачена, она должна быть либо явно
final
, либоeffectively final
. У новых «связываний» финальность появляется от рождения, что чуть отличается от захваченных переменных в лямбдах.
Но это исключительно к слову пришлось. :D
lany
27.11.2019 19:24+1Не туда копаешь. Неявный final внутри метода появился в multi-catch в седьмой джаве. Правда про это мало кто знает кроме разработчиков IDE.
Если же рассматривать объявление класса, то поля, объявленные в интерфейсе, неявно final (и static) с первой джавы. Ну и константы enum туда же.
orionll Автор
28.11.2019 06:26Да, что-то я в торопях даже не заметил, что про final откровенный бред написал. Я написал, что методы и классы по умолчанию являются final, что очевидно не так.
tsypanov
28.11.2019 12:34Ну, поля интерфейсов и перечисления я не упомянул как сами собой разумеющиеся вещи.
Неявный final внутри метода появился в multi-catch в седьмой джаве. Правда про это мало кто знает кроме разработчиков IDE.
Истину глаголешь, до твоего комментария я об этом не знал :)
Xobotun
27.11.2019 14:19А что произошло с областями видимости вот в этом примере: v?
if (!(obj instanceof String str)) { throw new RuntimeException(); } System.out.println(str.toLowerCase());
Скомпилировалось. <...>
"БиндингСвязывание паттерна"str
вышло за пределы фигурных скобок условного оператора?
То есть он работает иначе, чемtry-with-resources
, у которогоCloseable
-переменные, объявленные в круглых скобках, видны только внутри. Это тоже может доставить проблем, чувствую.orionll Автор
27.11.2019 14:27Связывание паттерна" str вышло за пределы фигурных скобок условного оператора?
Да, это называется flow scoping.
MaximChistov
27.11.2019 14:59+1Имхо не хватает еще вот такого случая в примерах:
Object obj = "a"; if (obj instanceof String s) { obj = "b"; System.out.println(s.toLowerCase()); }
Как сработает этот код? Не скомпилируется? Выдаст runtime error? Просто выведет «a»? Просто выведет «b»? Если выведет «a», то поведение уже не как у instnceof + cast, которое вывело бы значение obj на момент выполнения printlnmayorovp
27.11.2019 15:17+1Конечно же, поведение именно такое, как у instanceof + cast:
Object obj = "a"; if (obj instanceof String) { String s = (String)obj; obj = "b"; System.out.println(s.toLowerCase()); }
dim2r
27.11.2019 16:44Попробуйте еще тренарный оператор помучить. Что-то типа
int a = AA instanceof Integer aa? 1: null;orionll Автор
29.11.2019 10:16Это не очень интересно, потому что всё работает аналогично if. А вот что-нибудь другое помучить можно, например:
for (Object obj = ...; obj instanceof String str; obj = ...) { // Используем str }
Или вот такое:
final boolean ok = obj instanceof String str; if (ok) { System.out.println(str.toLowerCase()); }
Последнее, кстати, не работает.dim2r
29.11.2019 15:13работает аналогично if.
В тренарном есть такой финт, который компилируется, но вызывает ошибку исполнения:
int a=false?1:null;
если преобразовать в if, то не скомпируется
int a; if(false){a=1;}else{a=null;}
error: incompatible typeslany
30.11.2019 04:11В плане скоупинга переменных паттерна работает аналогично. Про вывод типов никто ничего не говорил.
fRoStBiT
27.11.2019 16:59Note: A.java uses unchecked or unsafe operations.
Похоже, опять придётся полагаться на инспекции в IDE.Поведение идентично unchecked cast, и реакция компилятора соответствующая, так что никаких проблем нет. Предупреждения компилятора надо всё-таки читать.
orionll Автор
27.11.2019 17:39Проблема есть. Она заключается во внешней обманчивости конструкции
instanceof List<Integer>
. Когда новичок смотрит на такое, то у него может возникнуть ложное впечатление, будто он на самом деле проверяет список на наличие только целых чисел в нём. Да, раньше было то же самое, но оно по крайней мере всё было явно: сначала проверяемinstanceof List
, а потом приводим объект кList<Integer>
. Сейчас всё это запрятано внутрь новогоinstanceof
'а и разобраться в деталях стало немного сложнее.fRoStBiT
28.11.2019 10:25ложное впечатление, будто он на самом деле проверяет список на наличие только целых чисел в нём
Но ведь это ничем не отличается от явного приведения:
final var intList = (List<Integer>) list;
Тоже создаёт "ложное впечатление, будто он на самом деле проверяет список на наличие только целых чисел в нём", ведь обычно приведение типа это делает (с массивами например).
Beholder
27.11.2019 21:00+1Эх, чего только не придумают, лишь бы на Kotlin не переходить...
if (a != null) { B b = a.getB(); if (b != null) { C c = b.getC(); if (c != null) { System.out.println(c.getSize()); } } }
Эквивалент:
a?.b?.c?.let { println(it.size) }
Ну или если с проверкой типов
(((a as? A)?.b as? B)?.c as? C)?.let { println(it.size) }
Не очень красиво, но можно ещё так:
inline fun <reified T> Any?.cast(): T? = this as? T a.cast<A>()?.b.cast<B>()?.c.cast<C>()?.let { println(it.size) }
koowaah
27.11.2019 22:22Спасибо за статью. Вот паттерн патчинг и в Java завозят. Хотелось также чтобы type test pattern также работал и с switch. Не хватает также deconstruction pattern и других паттернов. Но думаю, должны со временем завести.
Хотя многие паттерны можно реализовать и в виде библиотеки, но поддержка на уровне языка это круто.
snuk182
28.11.2019 01:25Немного оффтопный вопрос — а ничего не слышно про возможное появление в Java перегрузки операторов? Ведь по факту-то синтаксическая база для реализации перегрузки уже есть с появлением замыканий, и натянуть тот же подход на арифметические операторы, имхо, задача на порядки проще.
lany
28.11.2019 03:17Нет, пока не обсуждается. Честно говоря, мне кажется, что это плохая фича для джавы.
v2kxyz
28.11.2019 09:04+1А не расскажете кратко — почему это плохая идея? В Scala и Kotlin перегрузка операторов вроде нормально живет. На ум приходит только, то что появится новое поле для ошибок, но это почти с любой фичей так.
lany
29.11.2019 05:14-1Начнём с того, что ни у кого нет цели сделать из Джавы новый Котлин или Скалу. Какой смысл, если Котлин и Скала уже существуют? Джава — это отдельный язык со своей философией. У Котлина и Скалы другая философия. Это прекрасно, что у программистов есть выбор, какой философии следовать. Если сделать из Джавы второй Котлин, выбор пропадёт.
В Джаве есть одна хорошая вещь: в большинстве случаев локального контекста понятно, чтобы выяснить, что делает данная строчка кода. Скажем, если вы видите
a[b] = c * d
, вы знаете точно, что здесь у вас происходит запись произведения в элемент массива в куче, вне зависимости от того, что такое a, b, c и d, вне зависимости от того, какие у вас есть импорты и библиотеки, в каком классе вы выполняете этот код. Вы точно знаете, что в этой строчке у вас нет сетевого запроса или доступа к базе данных. В Котлине или Скале вы не уверены, эта строчка может делать абсолютно всё что угодно. Я считаю, что ясность — важная отличительная черта Джавы, и Джава перестанет быть Джавой, если потеряет ясность. Причём это усложнит не только чтение кода человеком, но и средства автоматического анализа кода. Некоторые вещи могут вообще перестать работать.v2kxyz
29.11.2019 15:37+3Причём это усложнит не только чтение кода человеком, но и средства автоматического анализа кода. Некоторые вещи могут вообще перестать работать.
Если честно, мне вот эту мысль в чуть более подробной форме ожидал увидеть. От кого же еще, как ни от вас.
Про философию я и раньше читал, но на то она и философия — очень субъективно.
Мне вот кажется, что код
читается хуже, чемa.set(b, c.multiply(b))
Особенно если формула вычислений подлиннее. И это стоит рисков по появлению нежелательных действий в перегруженных операторах. К тому же оператор + уже в каком то смысле перегружен, он означает и сложение и конкатенацию, а при помощи var можно вообщеa[b] = c * d
содомиюJavaScript устроить
Кстати var же ввели, на тему него тоже была куча холиваров, но нежелание писать boilerplate code победило желание иметь тотальную ясность, точнее ее отдали на откуп писателям.int a = 2; String b = "1"; var c = a + b; // c = "21", wtf?
P.S. Всякие мимокрокодилы, типа меня, про перегрузку операторов начали спрашивать еще до появления Scala и Kotlin, ибо приходили из С++.
P.P.S. Приду домой — загляну в IDEA с Kotlin, там нет фичи подсветки перегруженного оператора?
kacetal
28.11.2019 14:32Уже есть и давно, в восьмой джаве появилось.
mayorovp
28.11.2019 15:03Разве?
kacetal
28.11.2019 15:33Да, появилась возможность писать плагины к jvm. Например можно взять два BigDecimal a + b, а во время компилации это будет заменено на вызов метода add. Поищите, в интернете есть и видео и статьи как это делается, в том числе и на русском.
tsypanov
29.11.2019 12:27+1во время компилации
Не совсем понял вас: будет изменён байт-код, выданный javac-ом, или уже выход оптимизирующего компилятора?
kacetal
29.11.2019 20:33+1Во время компиляции компилятор проходит через 5-7 этапов (не помню точно) начиная с parse где происходит чтение исходников и заканчивая на generate где он генерирует class файлы, дак вот, можно вмешатся в некоторые стадии этого процесса и получить нужны нам class файл. Чтобы сделать это, необходимо имплементировать интерфейс Plugin.
https://docs.oracle.com/javase/8/docs/jdk/api/javac/tree/com/sun/source/util/Plugin.html
lany
28.11.2019 06:41Про перекрытие полей спросил. А про
|| false
— это в amber-dev можно спросить, там открытый мейлинг-лист.orionll Автор
28.11.2019 07:23Про
|| false
я подумал и похоже понял. С первого взгляда может показаться, что они эквиваленты с&& true
, однако это не так:
if (obj instanceof String str && <любое условие>) { // Можем всегда использовать str }
Но в случае с||
уже совсем другая картина:
if (obj instanceof String str || <любое условие>) { // Не можем использовать str }
Допустим, мы могли бы сделать по-умному, и во втором случае разрешить использоватьstr
, если<любое условие>
могло бы быть вычислено в false во время компиляции, но это было бы очень хрупко и делало бы опасным рефакторинг:
if (obj instanceof String str || false) { // str работает }
private boolean isFalse() { return false; } ... if (obj instanceof String str || isFalse()) { // str уже не работает }
Если программист захочет заинлайнитьisFalse()
, то код ломается.
dyadyaSerezha
28.11.2019 08:34А сдается мне, что это улучшит одну десятитысячеую часть кода, если не одну милионную. Приколько, но…
orionll Автор
28.11.2019 13:39Посмотрел наш проект (320k строк кода).
instanceof
используется 2290 раз. Почти все из них можно переписать на паттерн-матчинг.dyadyaSerezha
29.11.2019 02:57Один instanceof на каждые 150 строчек кода? А может, "что-то в консерватории подправить"?
lany
29.11.2019 05:09Зависит от проекта. В исходниках IntelliJ IDEA тоже многие тысячи instanceof. По сути дела многие инспекции — это поиск паттернов по дереву, паттерн-матчинг в чистом виде. Ну и в целом нет ничего плохого в instanceof, если правильно его использовать. Часто это существенно лучше, чем visitor pattern, который в джаве выглядит откровенно по-уродски, занимает больше места в исходниках и имеет больше накладных расходов при исполнении.
dyadyaSerezha
29.11.2019 06:04Все это так, но сколько таких проектов, как IntelliJ? Одна сотая? Вот и получается та самая одна десятитысячная.
tsypanov
29.11.2019 12:29На этой одной сотой держится вся прикладная разработка (и не только проектов на яве, кстати). Вы готовы пересесть на какой-нибудь "Саблайм" и запускать сборку/тесты каждый раз из командной строки? Я — нет. Посему, имхо, именно в этом месте лучше оставить как есть.
dyadyaSerezha
30.11.2019 02:14- IntelliJ уже написана.)
- Как я понимаю, все большая ее часть становится написана на Kotlin.
kacetal
28.11.2019 14:45Интересно, а если объявить такую переменную просто в теле метода, или можно только в условных операторах?
romankh3
Интересный подход к изучению нового функционала!
Продолжай в том же духе