В чем преимущество включения строк в Java 7?

19

Когда я начинал программировать на Java, тот факт, что операторы switch не принимают строки, расстраивал меня. Затем, используя Enums, я понял преимущества, которые вы получаете с ними, вместо того, чтобы передавать необработанные значения - безопасность типов (что упрощает рефакторинг), а также ясность для других разработчиков.

Я изо всех сил пытаюсь придумать ситуацию, когда с SE7 я теперь решу использовать переключатель со строками в качестве входов, а не Enums. Если они реализованы с помощью переключения целых строк (например, вместо частичных или регулярных выражений), кажется, что у кода меньше причин для изменения кода.

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

Какую пользу они приносят нам как программистам? Меньше котельной плиты?

Не похоже, что язык кричал об этой функции. Хотя, может быть, есть сценарий использования, который я пропускаю.

anotherdave
источник
Строки уже широко используются в выражениях switch, но поскольку люди не могут использовать их напрямую, они прибегают к обходным решениям, таким как гигантские деревья или преобразования в перечисления. Оба обходных пути делают код менее читабельным. Оба способа являются обходными путями, и зачем использовать обходной путь, если встроенное решение лучше показывает намерения программистов.
Питер Б
@PieterB Возможно, что Enum добавляет семантическое значение, а не обходной путь, что обеспечивает безопасность типов (например, опечатка будет обнаружена во время компиляции, а не приведет к ошибке). Это также позволяет нам проводить рефакторинг всех экземпляров строки, как это используется в этом контексте , не затрагивая другие случаи использования этой строки (что может случаться довольно часто с объектами домена.)
другой день

Ответы:

12

Насколько я могу судить, вопрос в том, почему включение констант String в enum не считается достаточным для удовлетворения потребностей языковых пользователей. Об этом говорится в официальном предложении, объявленном в списке рассылки JDK 7 (Project Coin).

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

ОСНОВНЫЕ ПРЕИМУЩЕСТВА: Что делает предложение благоприятным изменением?

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

ОСНОВНЫЕ ПРЕИМУЩЕСТВА: Почему платформа лучше, если предложение принято?

Потенциально лучшая производительность для строкового кода отправки.

ОСНОВНЫЕ НЕДОСТАТКИ: Всегда есть цена.

Некоторые усложнили реализацию и тестирование компилятора.

АЛЬТЕРНАТИВЫ: Можно ли получить преимущества и преимущества без изменения языка?

Нет; цепочечные тесты на равенство строк if-then-else являются потенциально дорогостоящими, и введение перечисления для его переключаемых констант, по одному на интересующее строковое значение, добавит другой тип в программу без веской причины ...

комар
источник
смелая часть довольно удивительно
Саймон Бергот
1
@ Симон хорошо для меня лично, это звучит как косвенная угроза: «Мы проиграем конкуренцию C #, если будем продолжать придерживаться до-Java5 способов справляться с подобными соображениями». :) Пример «путей до Java5» приведен в JDK-1223179: включить строки - представлен в 1995 году и быстро закрыт как « Не исправить» : «Не задерживайте дыхание. Ничего подобного в наших планах нет». «.
комнат
Спасибо, очень интересно увидеть причины, выдвинутые в предложении. Все еще неправильно говорить, что мы вводим тип в программу «без уважительной причины» - мы ОО-программисты! :)
anotherdave
5
@anotherdave "объектно-ориентированный" оправдывает введение типов только тогда, когда они служат содержательной цели. По-видимому, преобладающее мнение в JCP оказалось, что использование перечислений в качестве безмозглых оберток для строковых констант в switch не считается значимым
комнат
1
Вау, я удивлен (в хорошем смысле), чтобы увидеть официальное предложение, активно противодействующее типу раздувания. Это освежает. Тип bloat - огромная проблема в Java.
Бен Ли
7

Помимо того, что код становится более читабельным, существует потенциальный выигрыш в производительности по if/else ifсравнению со сравнениями. Стоит ли изменение, зависит от того, сколько сравнений вы бы сделали. Строковый переключатель будет выпущен как две отдельные инструкции переключения. Действует на первый хэш - кодов, так что он имеет тенденцию быть lookupswitch, в конечном счете , получая O(log n)сложность. Второе всегда идеально O(1) tableswitch, так что сложная сложность остается O(log n). Одиночная линейная цепочка if/else ifутверждений дала бы немного худшую O(n)сложность.

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

Майк Штробель
источник
1
Я больше спрашивал, почему я использовал переключатель на строке, а не на перечислении. Я бы посчитал большой if/elseблок плохой практикой, но на данный момент у меня не было бы особой причины его использовать.
anotherdave
Но тогда карта в сочетании с шаблоном команды даже увеличит читабельность при той же сложности, потому что у вас есть дополнительный тип команды
SpaceTrucker
3

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

Еще одна примечательная вещь в JDK 7 switch- это то, что он гораздо более перфоман, чем if-elseконструкции.

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

Андрей Чащев
источник
1
«Переключатель для строк незаменим, когда ваши значения перечисления приходят извне, то есть хранятся в базе данных». Можете ли вы уточнить это? Строки в операторе switch жестко запрограммированы: как только строки в базе данных будут изменены, ваш код устарел.
Джорджио
Что ж, вы правы - enumв этом случае можно использовать s из-за статической природы Java. Когда я писал это, я думал о Roleбиблиотеке безопасности, которая обычно предоставляется в виде класса и не может быть помещена в нее enum. Другой случай - это динамически скомпилированный код Java / Groovy - в этой ситуации, которая, однако, встречается редко, переключатели строк могут быть лучшим вариантом.
Андрей Чащев
2

Простота

Поддержка String в Switch полезна для обработки данных без преобразования в перечисление или if-elseлогику. Иногда просто включить String.

Из предложения функций в списке рассылки JDK 7 (Project Coin) ( @gnat answer )

введение перечисления для его переключаемых констант, по одному на интересующее строковое значение, добавит другой тип в программу без веской причины ...

Версия If-Else

Это коротко, но многие if'sтрудно читать. И это медленно.

if (color.equals("red")) {
    System.out.println("Color is Red");
} else if (color.equals("green")) {
    System.out.println("Color is Green");
} else {
    System.out.println("Color not found");
}

Enum версия

Перечисления должны быть определены, это хорошо, но иногда не нужно.

enum Color {RED, GREEN}

Обрабатывать как обычно

try {
    switch (Color.valueOf(color)) {
        case RED:
            System.out.println("Color is Red");
            break;
        case GREEN:
            System.out.println("Color is Green");
            break;
    }
} catch (IllegalArgumentException e) {
    System.out.println("Color not found");
}

JDK 7 - Строки в версии операторов Switch

Мы можем обрабатывать без конвертации и определения дополнительных типов.

switch (color) {
    case "red":
        System.out.println("Color is Red");
        break;
    case "green":
        System.out.println("Color is Green");
        break;
    default:
        System.out.println("Color not found");
}
MᴀʀɪᴜsᴢS
источник
7
Однако это не решает вопрос: почему вы используете здесь строки вместо перечислений?
Дейв Ньютон
0

Большинство улучшений на любом языке - сделать код проще для чтения. Я предпочитаю читаемый код над фантазией в любой день.

Вы можете не верить этому, но попробуйте:

public enum JettStaff {
  ADRIAN("Adrian German") {
    public String toString() {
      return name + " (dgerman@indiana.edu)";
    }
  },
  ARJIT("Arjit Sengupta") {
    public String toString() {
      return name + " (asengupt@indiana.edu)";
    }
  },

  // and on for the rest...

  private String name;

  public JettStaff(String n) { this.name = n; }
}

JettStaff x = JettStaff.SUZANNE;
System.out.println(x);

источник
Мне неясно, какой у вас пример кода против ваших комментариев выше. Вы имеете в виду Enum как пример чего-то, что трудно читать?
anotherdave
2
Это очень надумано; вы притворяетесь, что в enum не будет поля электронной почты или что это не класс.
Дейв Ньютон
4
@ user2860598 Если вы пытаетесь выразить свою точку зрения, используйте соответствующий пример. Это также не относится к аппликатуре строкой константы.
Дейв Ньютон
1
@ user2860598 Ответ, который вы связали, говорит, что они были введены и что ранее их можно было «приблизить» с помощью Enum. Моя точка зрения заключалась в том, что использование Enum кажется предпочтительным до сих пор, даже с их введением.
anotherdave
0

Перечисления великолепны, и вы должны использовать их вместо строк, если у вас есть такая возможность. Но бывают случаи, когда вы просто не можете, например, когда вам нужно работать с каким-то внешним объектом, приходящим извне Java. Представь, что тебе надо что-то разобрать. Например, ответ сервера, конфигурация, файл журнала или что-то подобное: у вас есть куча опций, которые вы ищете, и нет никаких способов перечислить их. Так что в Java <7 вы застряли бы с кучей

  if (something.equals("foo")) {
    // foo processing
  } else 
  if (something.equals("bar")) {
   // bar processing
  }

Вы все еще можете использовать перечисления в этом случае, просто пытаясь получить их по именам и / или предоставив пользовательскую подпрограмму String-> Enum, но иногда это невозможно или просто нецелесообразно (например, когда вам нужно создать слишком много перечислений)

TLDR : вам обязательно следует использовать Enums, когда вы работаете исключительно с Java-кодом, но это не всегда возможно с внешними объектами. Я надеюсь, что мое объяснение имеет смысл.


источник
Если вы имеете дело с внешними данными, и вы уже знаете достаточно, чтобы знать, что вам нужно в ваших ifутверждениях, то вам все равно следует обернуть их , что означает, что вы все равно можете использовать перечисления или шаблон команд и т. Д.
Дейв Ньютон
@DaveNewton: да, вы все еще можете использовать перечисления, но, как я уже сказал в своем ответе, иногда это не практично, как в случае, когда вы в конечном итоге создаете множество перечислений, которые будут использоваться только один раз в вашем коде во время синтаксического анализа.
@dimoniy Но если бы вам пришлось создавать сотни значений Enum, разве это не означало бы, что с альтернативой switch вам понадобилось бы иметь сотни выражений case? Если есть так много переменных, я бы определенно предпочел безопасность типов перечислений.
anotherdave
0

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

Если у вас есть некоторые жестко закодированные значения, которые вы делаете в операторах if-elseif, использование постоянных примитивных значений для них намного лучше, чем в Java 1.5, только потому, что это обеспечивает безопасность типов.

ОК, будут ситуации, когда вы получите строковые значения (из HTTP-запроса, файлов и т. Д.), И преобразование их в примитивные значения - большая проблема. Но Enumс этой точки зрения все хорошо. Потому что, когда вы хотите изменить значение, которое вы храните в Enum, просто измените его при объявлении. Нет необходимости изменять что-либо еще в вашем коде. (Вам, конечно, нужно изменить значения, хранящиеся где-то еще).

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


источник
-1

Если вы знаете, какая информация поступает и что она может иметь только 5 состояний, тогда используйте Enum. Однако, если возможные состояния могут быть больше 9000, и вам нужно только найти 42, тогда лучше использовать переключатель, потому что вы не хотите выводить все эти состояния.

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

Но почему они ввели это сейчас? Это было просто изменение, которое позволило более чистый код.

В версии 1.5 они также позволяли вам создавать собственные тела для Enums.


источник
Но не могли бы вы просто присвоить другим 8958 возможным значениям одно состояние Enum?
anotherdave
@anotherdave Это где defaultв switchприходит к игре. Я не знаю, что вы можете иметь состояние по умолчанию с Enum, если вы не установите состояние по умолчанию, но тогда этот код просто раздут, тогда как использование a switchнамного чище.
3
Кажется странным сказать, что UNKNOWNсостояние в Enum является раздутым, а defaultслучай с переключателем - нет.
anotherdave