Почему я не могу использовать оператор switch на String?

Будет ли эта функциональность переведена в более позднюю версию Java?

Может ли кто-нибудь объяснить, почему я не могу это сделать, как в техническом руководстве оператора Java switch?

Ответы

Ответ 1

Операторы switch с String случаями были реализованы в Java SE 7, не менее 16 лет после того, как они были сначала запрошены. Ясная причина задержки не была предоставлена, но она, вероятно, имела отношение к производительности.

Реализация в JDK 7

Эта функция теперь реализована в javac с процессом "обезжиривания" ; чистый синтаксис высокого уровня с использованием String константы в объявлениях case расширяется во время компиляции в более сложный код, следуя шаблону. В результате код использует команды JVM, которые всегда существовали.

A switch с событиями String в процессе компиляции преобразуется в два ключа. Первая отображает каждую строку в уникальное целое число - его положение в исходном коммутаторе. Это делается путем первого включения хэш-кода метки. Соответствующим случаем является оператор if, который проверяет равенство строк; если в хеше есть столкновение, тест является каскадным if-else-if. Второй переключатель отражает это в исходном исходном коде, но заменяет метки меток соответствующими позициями. Этот двухэтапный процесс упрощает сохранение управления потоком исходного переключателя.

Переключение в JVM

Для большей технической глубины на switch вы можете обратиться к спецификации JVM, где описывается компиляция операторов switch отличную статью, которая более подробно описывает это, а также скрытый взгляд на другие инструкции управления потоком Java.

До JDK 7

До JDK 7 enum может аппроксимировать переключатель String. Это использует статический valueOf метод, сгенерированный компилятором для каждого типа enum. Например:

Pill p = Pill.valueOf(str);
switch(p) {
  case RED:  pop();  break;
  case BLUE: push(); break;
}

Ответ 2

Если у вас есть место в вашем коде, где вы можете включить String, тогда может быть лучше реорганизовать String как перечисление возможных значений, которые вы можете включить. Конечно, вы ограничиваете потенциальные значения строк, которые вы можете иметь, для тех, которые перечислены, что может быть или не быть желательным.

Конечно, в вашем перечислении может быть запись для "другого" и метода 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;
}

Ответ 3

Ниже приведен полный пример, основанный на записи JeeBee, с использованием java enum вместо использования настраиваемого метода.

Обратите внимание, что в 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
  }
}

Ответ 4

Коммутаторы на основе целых чисел могут быть оптимизированы для очень эффективного кода. Переключатели, основанные на другом типе данных, могут быть скомпилированы только в ряд операторов if().

По этой причине C и С++ разрешают только переключатели на целочисленных типах, поскольку это было бессмысленно с другими типами.

Дизайнеры С# решили, что стиль важен, даже если не было никакого преимущества.

Дизайнеры Java, по-видимому, думали, как дизайнеры C.

Ответ 5

Джеймс Карран лаконично говорит: "Переключатели на основе целых чисел могут быть оптимизированы для очень эффективного кода. Коммутаторы на основе другого типа данных могут быть скомпилированы только в ряд операторов if(). По этой причине C и С++ разрешают только включение целочисленные типы, поскольку это было бессмысленно с другими типами."

Мое мнение, и только в том, что, как только вы начнете включать не-примитивы, вам нужно начать думать о "равных" по сравнению с "==". Во-первых, сравнение двух строк может быть довольно продолжительной процедурой, добавляя к проблемам производительности, о которых говорилось выше. Во-вторых, если есть включение строк, будет требоваться включение строк, игнорирующих регистр, включение строк с учетом/игнорирование локали, включение строк на основе регулярных выражений.... Я бы одобрил решение, которое сэкономило много времени для языковых разработчиков за небольшим промежутком времени для программистов.

Ответ 6

Пример прямого использования 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;
    }

}

Ответ 7

Помимо приведенных выше хороших аргументов, я добавлю, что многие люди сегодня видят switch как устаревшую оставшуюся часть процессуального прошлого Java (обратно в C раз).

Я не полностью разделяю это мнение, я думаю, что switch может иметь свою полезность в некоторых случаях, по крайней мере из-за его скорости, и в любом случае это лучше, чем некоторые серии каскадных числовых else if, которые я видел в некоторых код...

Но, действительно, стоит посмотреть на случай, когда вам нужен переключатель, и посмотреть, не может ли он быть заменен чем-то большим OO. Например, перечисления в Java 1.5+, возможно, HashTable или какая-то другая коллекция (когда-то я сожалею, что у нас нет (анонимных) функций как гражданина первого класса, как в Lua - у которого нет ключа - или JavaScript) или даже полиморфизма.

Ответ 8

Если вы не используете JDK7 или выше, вы можете использовать hashCode() для имитации. Поскольку String.hashCode() обычно возвращает разные значения для разных строк и всегда возвращает равные значения для равных строк, он достаточно надежный (разные строки могут выдавать один и тот же хеш-код как @Lii, упомянутый в комментарии, например, "FB" и "Ea" "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;
            }
        }
    }
}

Ответ 9

В течение многих лет мы использовали препроцессор (n open source) для этого.

//#switch(target)
case "foo": code;
//#end

Предварительно обработанные файлы называются Foo.jpp и обрабатываются в Foo.java с помощью ant script.

Преимущество заключается в том, что он обрабатывается в Java, который работает на 1.0 (хотя обычно мы поддерживаем только 1.4). Также было намного проще сделать это (много строковых переключателей) по сравнению с fudging его с перечислениями или другими обходными решениями - код был намного легче читать, поддерживать и понимать. IIRC (на данный момент не может предоставить статистику или технические аргументы), она также была быстрее, чем естественные эквиваленты Java.

Недостатки в том, что вы не редактируете Java, так что это немного больше рабочего процесса (редактирование, процесс, компиляция/тестирование), а среда IDE свяжется с Java, которая немного запутана (коммутатор становится серией if/else логические шаги) и порядок расположения переключателя не поддерживается.

Я бы не рекомендовал его для 1.7+, но он полезен, если вы хотите запрограммировать Java, который нацелен на более ранние JVM (поскольку Joe public редко имеет самую последнюю установленную).

Вы можете получить из SVN или просмотреть код онлайн. Вам понадобится EBuild, чтобы создать его как есть.

Ответ 10

Другие ответы говорят, что это было добавлено в Java 7 и заданы обходные пути для более ранних версий. Этот ответ пытается ответить на вопрос "почему"

Java была реакцией на чрезмерные сложности С++. Он был разработан как простой чистый язык.

В языке String появилось немного обработки особых случаев на языке, но мне кажется очевидным, что дизайнеры пытались свести к минимуму количество специального корпуса и синтаксического сахара.

включение строк довольно сложно под капотом, поскольку строки не являются простыми примитивными типами. Это была не общая особенность в то время, когда Java была разработана и не очень хорошо вписывается в минималистский дизайн. Тем более, что они решили не использовать специальный случай == для строк, это было бы (и есть) немного странно для случая, чтобы работать там, где == нет.

Между 1.0 и 1.4 сам язык оставался почти таким же. Большинство усовершенствований Java были на стороне библиотеки.

Все, что изменилось с помощью Java 5, существенно расширилось. Дальнейшие расширения выполнялись в версиях 7 и 8. Я ожидаю, что это изменение отношения было вызвано повышением С#

Ответ 11

Не очень красиво, но вот еще один способ для 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);

Ответ 12

Это бриз в Groovy; Я вставляю java groovy и создаю класс утилиты groovy, чтобы делать все эти вещи и многое другое, которое я нахожу раздражающим в Java (поскольку я застрял с использованием Java 6 на предприятии).

it.'p'.each{
switch ([email protected]()){
   case "choclate":
     myholder.myval=(it.text());
     break;
     }}...

Ответ 13

Когда вы используете intellij, также смотрите:

Файл → Структура проекта → Проект

Файл → Структура проекта → Модули

Если у вас несколько модулей, убедитесь, что вы установили правильный языковой уровень на вкладке модуля.

Ответ 14

public class StringSwitchCase { 

    public static void main(String args[]) {

        visitIsland("Santorini"); 
        visitIsland("Crete"); 
        visitIsland("Paros"); 

    } 

    public static void visitIsland(String island) {
         switch(island) {
          case "Corfu": 
               System.out.println("User wants to visit Corfu");
               break; 
          case "Crete": 
               System.out.println("User wants to visit Crete");
               break; 
          case "Santorini": 
               System.out.println("User wants to visit Santorini");
               break; 
          case "Mykonos": 
               System.out.println("User wants to visit Mykonos");
               break; 
         default: 
               System.out.println("Unknown Island");
               break; 
         } 
    } 

}