Будет ли эта функциональность реализована в более поздней версии Java?
Может ли кто-нибудь объяснить, почему я не могу этого сделать, например, технический способ работы оператора switch в Java?
Будет ли эта функциональность реализована в более поздней версии Java?
Может ли кто-нибудь объяснить, почему я не могу этого сделать, например, технический способ работы оператора switch в Java?
Операторы переключения с String случаями были реализованы в Java SE 7 в течение как минимум 16 лет после их первого запроса. Четкой причины задержки не было указано, но, скорее всего, это было связано с производительностью.
Эта функция теперь реализована в javac процессе "удаления сахара"; чистый , высокоуровневый синтаксис с использованием String констант в case объявлениях расширяется во время компиляции в более сложный код, следующий шаблону. Результирующий код использует инструкции JVM, которые существовали всегда.
switch с String регистрами преобразуется в два переключателя во время компиляции. Первый отображает каждую строку в уникальное целое число в исходном переключателе. Это делается путем первого включения хэш-кода метки. Соответствующий случай - это оператор if, который проверяет равенство строк; если в хэше есть коллизии, тест будет каскадным if-else-if. Второй переключатель отражает это в исходном исходном коде, но заменяет метки case их соответствующими позициями. Этот двухэтапный процесс позволяет легко сохранить управление потоком исходного коммутатора.
Для получения дополнительной технической информации о switch вы можете обратиться к Спецификации JVM, где описывается компиляция операторов switch. Вкратце, есть две разные инструкции JVM, которые можно использовать для переключения, в зависимости от разреженности констант, используемых в случаях. Оба зависят от использования целочисленных констант для каждого случая для эффективного выполнения.
Если константы плотные, они используются в качестве индекса (после вычитания наименьшего значения) в таблице указателей инструкций инструкции tableswitch.
Если константы редкие, выполняется двоичный поиск правильного регистра с помощью инструкции lookupswitch.
При удалении сахара switch на String объекты, вероятно, будут использоваться обе инструкции. lookupswitch подходит для первого включения хэш-кодов, чтобы найти исходное положение корпуса. Полученный порядковый номер естественным образом подходит для tableswitch.
Обе инструкции требуют, чтобы целочисленные константы, присвоенные каждому случаю, были отсортированы во время компиляции. Во время выполнения, хотя O(1) производительность tableswitch в целом кажется лучше, чем O(log(n)) производительность lookupswitch, требуется некоторый анализ, чтобы определить, достаточно ли плотна таблица, чтобы оправдать компромисс между пространством и временем. Билл Веннерс написал отличную статью, в которой этот вопрос рассматривается более подробно, а также -hood посмотрите другие инструкции управления потоком Java.
До JDK 7 enum мог приблизительно соответствовать коммутатору на основе String. Здесь используется статический _22 _ метод, сгенерированный компилятором для каждого enum типа. Например:
Pill p = Pill.valueOf(str);
switch(p) {
case RED: pop(); break;
case BLUE: push(); break;
}
Pill для выполнения некоторых действий на основе str, я бы сказал, что предпочтительнее if-else, поскольку он позволяет обрабатывать str значения за пределами диапазона КРАСНОГО, СИНЕГО без необходимости перехватывать исключение из valueOf или вручную проверять совпадение против имени каждого типа перечисления, что просто добавляет ненужные накладные расходы. По моему опыту, использование valueOf для преобразования в перечисление имело смысл только в том случае, если позже потребовалось безопасное для типов представление значения String.
- person MilesHampson; 23.07.2013
(hash >> x) & ((1<<y)-1) даст разные значения для каждой строки, у которой hashCode отличается, а (1<<y) меньше, чем в два раза, количество строк (или, по крайней мере, не намного больше).
- person supercat; 18.12.2013
Если у вас есть место в вашем коде, где вы можете включить String, тогда может быть лучше реорганизовать String, чтобы он был перечислением возможных значений, которые вы можете включить. Конечно, вы ограничиваете потенциальные значения String, которые вы можете иметь, значениями в перечислении, что может быть, а может и не быть желательным.
Конечно, ваше перечисление может иметь запись для 'other' и метод fromString (String), тогда у вас может быть
ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
case MILK: lap(); break;
case WATER: sip(); break;
case BEER: quaff(); break;
case OTHER:
default: dance(); break;
}
Ниже приведен полный пример, основанный на сообщении JeeBee, с использованием перечисления java вместо использования настраиваемого метода.
Обратите внимание, что в Java SE 7 и более поздних версиях вы можете вместо этого использовать объект String в выражении оператора switch.
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String current = args[0];
Days currentDay = Days.valueOf(current.toUpperCase());
switch (currentDay) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
System.out.println("boring");
break;
case THURSDAY:
System.out.println("getting better");
case FRIDAY:
case SATURDAY:
case SUNDAY:
System.out.println("much better");
break;
}
}
public enum Days {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
}
Переключатели, основанные на целых числах, можно оптимизировать для получения очень эффективного кода. Переключатели, основанные на другом типе данных, могут быть скомпилированы только в серию операторов if ().
По этой причине C и C ++ допускают переключение только на целочисленные типы, поскольку с другими типами это было бессмысленно.
Разработчики C # решили, что стиль важен, даже если в нем нет преимущества.
Создатели Java, по-видимому, думали, как дизайнеры C.
Джеймс Карран кратко говорит: «Переключатели, основанные на целых числах, могут быть оптимизированы для получения очень эффективного кода. Переключатели, основанные на другом типе данных, могут быть скомпилированы только в серию операторов if (). По этой причине C и C ++ допускают переключение только на целочисленные типы, поскольку с другими типами это было бессмысленно ".
Мое мнение, и оно только так, заключается в том, что как только вы начинаете включать непримитивы, вам нужно думать о «равных» и «==». Во-первых, сравнение двух строк может быть довольно длительной процедурой, что усугубляет проблемы с производительностью, упомянутые выше. Во-вторых, если есть переключение строк, будет требоваться включение строк без учета регистра, включение строк с учетом / игнорирование языкового стандарта, переключение строк на основе регулярного выражения .... Я бы одобрил решение, которое сэкономило много времени для языковых разработчиков за счет небольшого количества времени для программистов.
matched и not matched. (Однако не принимая во внимание такие вещи, как [named] groups / и т. Д.)
- person JAB; 30.07.2012
Также может быть показан пример прямого использования String начиная с версии 1.7:
public static void main(String[] args) {
switch (args[0]) {
case "Monday":
case "Tuesday":
case "Wednesday":
System.out.println("boring");
break;
case "Thursday":
System.out.println("getting better");
case "Friday":
case "Saturday":
case "Sunday":
System.out.println("much better");
break;
}
}
Помимо приведенных выше хороших аргументов, я добавлю, что многие люди сегодня видят switch как устаревший остаток процедурного прошлого Java (назад во времена C).
Я не полностью разделяю это мнение, я думаю, что switch может быть полезным в некоторых случаях, по крайней мере, из-за его скорости, и в любом случае это лучше, чем некоторые серии каскадных числовых else if, которые я видел в каком-то коде ...
Но действительно, стоит посмотреть на случай, когда вам нужен переключатель, и посмотреть, нельзя ли его заменить чем-то более OO. Например, перечисления в Java 1.5+, возможно, HashTable или какая-то другая коллекция (иногда я сожалею, что у нас нет (анонимных) функций в качестве первоклассного гражданина, как в Lua - у которого нет переключателя - или JavaScript) или даже полиморфизма.
Если вы не используете JDK7 или выше, вы можете использовать hashCode() для имитации. Поскольку String.hashCode() обычно возвращает разные значения для разных строк и всегда возвращает одинаковые значения для одинаковых строк, это довольно надежно (разные строки могут создавать тот же хэш-код, что и @Lii, упомянутый в комментарии, например "FB" и "Ea") См. документация.
Итак, код будет выглядеть так:
String s = "<Your String>";
switch(s.hashCode()) {
case "Hello".hashCode(): break;
case "Goodbye".hashCode(): break;
}
Таким образом, вы технически включаете int.
В качестве альтернативы вы можете использовать следующий код:
public final class Switch<T> {
private final HashMap<T, Runnable> cases = new HashMap<T, Runnable>(0);
public void addCase(T object, Runnable action) {
this.cases.put(object, action);
}
public void SWITCH(T object) {
for (T t : this.cases.keySet()) {
if (object.equals(t)) { // This means that the class works with any object!
this.cases.get(t).run();
break;
}
}
}
}
case, как я думал, всегда должны быть постоянными значениями, а String.hashCode() не таковыми (даже если на практике расчет между JVM никогда не менялся).
- person StaxMan; 26.01.2018
case не обязательно должны определяться во время компиляции, поэтому он работает нормально.
- person hyper-neutrino; 26.01.2018
В течение многих лет мы использовали для этого препроцессор с открытым исходным кодом.
//#switch(target)
case "foo": code;
//#end
Предварительно обработанные файлы называются Foo.jpp и обрабатываются в Foo.java с помощью ant-скрипта.
Преимущество в том, что он переработан в Java, работающую на 1.0 (хотя обычно мы поддерживали только обратно до 1.4). К тому же это было намного проще (много переключателей строк), чем придумывать перечисления или другие обходные пути - код было намного легче читать, поддерживать и понимать. IIRC (на данный момент не может предоставить статистику или технические рассуждения), он также был быстрее, чем естественные эквиваленты Java.
Недостатки в том, что вы не редактируете Java, поэтому это немного больше рабочего процесса (редактирование, обработка, компиляция / тестирование), плюс IDE будет связываться с Java, которая немного запутана (переключатель становится серией логических шагов if / else) и порядок корпуса переключателя не сохраняется.
Я бы не рекомендовал его для версии 1.7+, но он полезен, если вы хотите запрограммировать Java, ориентированную на более ранние JVM (поскольку Joe public редко устанавливает последнюю версию).
Вы можете получить его из SVN или просмотрите код в Интернете. Вам понадобится EBuild, чтобы собрать его как есть.
В других ответах говорится, что это было добавлено в Java 7 и даны обходные пути для более ранних версий. Этот ответ пытается ответить на вопрос «почему».
Java была реакцией на чрезмерную сложность C ++. Он был разработан как простой чистый язык.
Строка получила небольшую обработку особых случаев в языке, но мне кажется очевидным, что дизайнеры пытались свести к минимуму количество специального корпуса и синтаксического сахара.
включение строк довольно сложно, поскольку строки не являются простыми примитивными типами. В то время, когда разрабатывалась Java, это не было обычным явлением и не очень хорошо вписывалось в минималистский дизайн. Тем более, что они решили не использовать специальный случай == для строк, было бы (и есть) немного странно, если бы случай работал там, где == не работает.
Между 1.0 и 1.4 сам язык практически не изменился. Большинство улучшений Java касались библиотеки.
Все изменилось с выходом Java 5, язык был существенно расширен. Дальнейшие расширения последовали в версиях 7 и 8. Я ожидаю, что это изменение отношения было вызвано появлением C #.
Технические детали были хорошо объяснены в этом ответе. Я просто хотел добавить это с помощью выражений переключателя Java 12 вы можете сделать это со следующим синтаксисом:
String translation(String cat_language) {
return switch (cat_language) {
case "miau miau" -> "I am to run";
case "miauuuh" -> "I am to sleep";
case "mi...au?" -> "leave me alone";
default -> "eat";
};
}
JEP 354: переключение выражений (предварительная версия) в JDK-13 и JEP 361: переключение выражений (стандартное) в JDK-14 будет расширьте оператор switch, чтобы его можно было использовать как выражение.
Теперь вы можете:
case L ->): # P3 #
# P4 #
Итак, демонстрация из ответов (1, 2) может выглядеть так:
public static void main(String[] args) {
switch (args[0]) {
case "Monday", "Tuesday", "Wednesday" -> System.out.println("boring");
case "Thursday" -> System.out.println("getting better");
case "Friday", "Saturday", "Sunday" -> System.out.println("much better");
}
В Java 11+ это возможно и с переменными. Единственное условие - он должен быть постоянным.
Например:
final String LEFT = "left";
final String RIGHT = "right";
final String UP = "up";
final String DOWN = "down";
String var = ...;
switch (var) {
case LEFT:
case RIGHT:
case DOWN:
default:
return 0;
}
PS. Я не пробовал этого с более ранними jdks. Поэтому обновите ответ, если он там тоже поддерживается.
Не очень красиво, но вот другой способ для Java 6 и ниже:
String runFct =
queryType.equals("eq") ? "method1":
queryType.equals("L_L")? "method2":
queryType.equals("L_R")? "method3":
queryType.equals("L_LR")? "method4":
"method5";
Method m = this.getClass().getMethod(runFct);
m.invoke(this);
В Groovy это совсем несложно; Я встраиваю отличную банку и создаю служебный класс groovy, чтобы делать все эти и многое другое, что мне неприятно делать в Java (поскольку я застрял на Java 6 на предприятии).
it.'p'.each{
switch ([email protected]()){
case "choclate":
myholder.myval=(it.text());
break;
}}...
Когда вы используете intellij, посмотрите также:
Файл -> Структура проекта -> Проект
Файл -> Структура проекта -> Модули
Если у вас несколько модулей, убедитесь, что вы установили правильный уровень языка на вкладке модулей.
"Don't hold your breath."lol, bugs.sun.com/bugdatabase/ view_bug.do?bug_id=1223179 - person raffian   schedule 11.08.2013