Сегодня мой коллега предложил мне реорганизовать мой код, чтобы использовать оператор метки для управления потоком через 2 вложенных цикла for, которые я создал. Я никогда не использовал их раньше, потому что лично я думаю, что они ухудшают читабельность программы. Однако я готов изменить свое мнение об их использовании, если аргумент достаточно веский. Каково мнение людей о заявлениях лейбла?
Следует ли мне избегать использования выражений Java Label?
Ответы (11)
Многие алгоритмы выражаются легче, если вы можете перейти через два цикла (или цикл, содержащий оператор switch). Не расстраивайся из-за этого. С другой стороны, это может указывать на слишком сложное решение. Так что отойдите в сторону и посмотрите на проблему.
Некоторые люди предпочитают подход «один вход, один выход» для всех циклов. То есть избегать разрыва (и продолжения) и раннего возврата для циклов в целом. Это может привести к дублированию кода.
Чего я бы категорически избегал, так это введения вспомогательных переменных. Сокрытие потока управления внутри состояния добавляет путаницы.
Разделение помеченных циклов на два метода может быть затруднено. Исключения, вероятно, слишком тяжеловесны. Попробуйте подход с одним входом и одним выходом.
Ярлыки похожи на goto: используйте их с осторожностью и только тогда, когда они сделают ваш код быстрее, и что более важно, более понятным,
например, если вы находитесь в больших циклах глубиной в шесть уровней и сталкиваетесь с условием, которое делает оставшуюся часть цикла бессмысленной для завершения, нет смысла иметь 6 дополнительных ловушек в ваших операторах условий для преждевременного выхода из цикла.
Ярлыки (и goto) не зло, просто иногда люди используют их во вред. Большую часть времени мы на самом деле пытаемся написать наш код, чтобы он был понятен вам и следующему программисту, который придет вам на помощь. Сделать его сверхбыстрым — второстепенная задача (остерегайтесь преждевременной оптимизации).
Когда метки (и goto) используются неправильно, они делают код менее читабельным, что вызывает огорчение у вас и у следующего разработчика. Компилятору все равно.
Есть несколько случаев, когда вам нужны метки, и они могут сбивать с толку, потому что они редко используются. Однако, если вам нужно использовать один, используйте один.
Кстати: это компилируется и запускается.
class MyFirstJavaProg {
public static void main(String args[]) {
http://www.javacoffeebreak.com/java101/java101.html
System.out.println("Hello World!");
}
}
Мне любопытно услышать, какая у вас альтернатива ярлыкам. Я думаю, что это в значительной степени сведется к аргументу «возврат как можно раньше» против «использовать переменную для хранения возвращаемого значения и возвращать только в конце».
Метки довольно стандартны, когда у вас есть вложенные циклы. Единственный способ, которым они действительно ухудшают читабельность, — это когда другой разработчик никогда раньше их не видел и не понимает, что они означают.
Я использовал цикл с меткой Java для реализации метода Sieve для поиска простых чисел (сделано для одной из математических задач проекта Эйлера), что сделало его в 10 раз быстрее по сравнению с вложенными циклами. Например, если (определенное условие) вернуться к внешнему циклу.
private static void testByFactoring() {
primes: for (int ctr = 0; ctr < m_toFactor.length; ctr++) {
int toTest = m_toFactor[ctr];
for (int ctr2 = 0; ctr2 < m_divisors.length; ctr2++) {
// max (int) Math.sqrt(m_numberToTest) + 1 iterations
if (toTest != m_divisors[ctr2]
&& toTest % m_divisors[ctr2] == 0) {
continue primes;
}
} // end of the divisor loop
} // end of primes loop
} // method
Я спросил программиста на C++, насколько плохи помеченные циклы, он сказал, что будет использовать их экономно, но иногда они могут пригодиться. Например, если у вас есть 3 вложенных цикла и при определенных условиях вы хотите вернуться к самому внешнему циклу.
Таким образом, у них есть свое применение, это зависит от проблемы, которую вы пытались решить.
Я никогда не видел, чтобы метки использовались «в дикой природе» в коде Java. Если вы действительно хотите разорвать вложенные циклы, посмотрите, можете ли вы реорганизовать свой метод, чтобы ранний оператор возврата делал то, что вы хотите.
Технически, я думаю, нет большой разницы между ранним возвращением и лейблом. Однако практически каждый Java-разработчик видел раннее возвращение и знает, что он делает. Я предполагаю, что многие разработчики, по крайней мере, были бы удивлены ярлыком и, вероятно, были бы сбиты с толку.
В школе меня учили ортодоксальности единого входа/единого выхода, но с тех пор я стал ценить ранние операторы возврата и выход из циклов как способ упростить код и сделать его более понятным.
Я бы поспорил в их пользу в некоторых местах, я нашел их особенно полезными в этом примере:
nextItem: for(CartItem item : user.getCart()) {
nextCondition : for(PurchaseCondition cond : item.getConditions()) {
if(!cond.check())
continue nextItem;
else
continue nextCondition;
}
purchasedItems.add(item);
}
Я думаю, что с новым циклом for-each метка может быть действительно ясной.
Например:
sentence: for(Sentence sentence: paragraph) {
for(String word: sentence) {
// do something
if(isDone()) {
continue sentence;
}
}
}
Я думаю, что это выглядит очень ясно, если ваша метка совпадает с вашей переменной в новом для каждого. На самом деле, возможно, Java должна быть злой и добавить неявные метки для каждой переменной, хех
continue sentence;
не продолжает обрабатывать предложение, слова которого (которое?) были обработаны внутренним циклом: он продолжает обработку _следующего предложения_/того же абзаца.
- person greybeard; 15.01.2016
Я никогда не использую метки в своем коде. Я предпочитаю создавать защиту и инициализировать ее значением null или другим необычным значением. Эта защита часто является объектом результата. Я не видел, чтобы кто-то из моих коллег использовал ярлыки, и не нашел их в нашем репозитории. Это действительно зависит от вашего стиля кодирования. На мой взгляд, использование меток ухудшит читабельность, поскольку это необычная конструкция и обычно она не используется в Java.
Да, вам следует избегать использования меток, если нет особой причины для их использования (уместен пример упрощения реализации алгоритма). В таком случае я бы посоветовал добавить достаточные комментарии или другую документацию, чтобы объяснить причину этого, чтобы кто-то не пришел позже и не исказил его из-за какого-то понятия «улучшение кода», «избавления от запаха кода» или какое-то другое потенциально BS оправдание.
Я бы приравнял этот вопрос к решению, когда следует или не следует использовать тернарный оператор if. Основная причина заключается в том, что это может ухудшить читабельность, и если программист не будет очень осторожен в том, чтобы именовать вещи разумным образом, то использование соглашений, таких как метки, может значительно ухудшить ситуацию. Предположим, что в примере с использованием «nextCondition» и «nextItem» для имен меток использовались «loop1» и «loop2».
Лично для меня метки — это одна из тех функций, которые не имеют для меня большого смысла, за исключением ассемблера, Бейсика и других языков с аналогичными ограничениями. В Java есть множество более традиционных/регулярных циклов и управляющих конструкций.
Я обнаружил, что метки иногда бывают полезны в тестах, чтобы разделить обычную настройку, упражнение и этапы проверки, а также сгруппировать связанные операторы. Например, используя терминологию BDD:
@Test
public void should_Clear_Cached_Element() throws Exception {
given: {
elementStream = defaultStream();
elementStream.readElement();
Assume.assumeNotNull(elementStream.lastRead());
}
when:
elementStream.clearLast();
then:
assertThat(elementStream.lastRead()).isEmpty();
}
Варианты форматирования могут различаться, но основная идея заключается в том, что метки в этом случае обеспечивают заметное различие между логическими разделами, составляющими тест, лучше, чем комментарии. Я думаю, что библиотека Spock просто использует эту функцию для объявления фаз тестирования.