Яка перевага від вмикання рядків у Java 7?


19

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

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

І з інструментами IDE та коефіцієнтом читання-запису кодування я був би набагато щасливішим автоматично генерувати додатковий Enum, ніж обходити рядкові значення.

Яку користь вони приносять нам програмістам? Менше котла-плити?

Не здається, що мова кричала за цією функцією. Хоча, можливо, є корисний випадок, який я оглядаю.


Струни вже дуже часто використовуються в операторах переключення, але, оскільки люди не можуть їх безпосередньо використовувати, вони вдаються до обхідних шляхів, як гігантські, якщо ще дерева або перетворення на перерахунки. Обидва способи обходу роблять код менш читабельним. Обидва способи є обхідними шляхами, і чому використовувати вирішення, якщо рішення, яке реалізується на самоті, показує наміри програмістів краще.
Пітер Б

@PieterB Хоча, можливо, Enum додає смислового значення, а не обхідного способу, хоча це забезпечує безпеку типу (наприклад, помилка друку буде зафіксована під час компіляції, а не призводить до помилок). Це також дозволяє нам перефактурувати всі екземпляри рядка, що використовуються в цьому контексті , не впливаючи на інші звичаї цього рядка (що може траплятися досить часто з об’єктами домену.)
elsedave

Відповіді:


12

Наскільки я можу сказати, питання полягає в тому, чому загортання струнних констант у перерахунки не вважалося достатнім для задоволення потреб користувачів мови. Це було розглянуто в офіційній пропозиції щодо функцій, оголошеній у списку розсилки JDK 7 (проект "Монета").

Під час мого прочитання пропозиції альтернатива використання переписок була відхилена на підставі того, що вона вводить типи роздуття. Для вашої зручності відповідна частина пропозиції цитується нижче, із заявою, що стосується перерахунків, цитується жирним шрифтом :

ОСНОВНА ПРАВА: Що робить пропозицію сприятливою зміною?

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

ОСНОВНА ПЕРЕВАГА: Чому платформа краще, якщо пропозиція буде прийнята?

Потенційно краща продуктивність для рядкового коду відправки.

ОСНОВНІ ВІДХОДЖЕННЯ: Витрати завжди є.

Деякі збільшили складність реалізації та тестування для компілятора.

АЛЬТЕРНАТИВИ: Чи можна мати користь і переваги без зміни мови?

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


смілива частина досить дивно
Саймон Бергот

1
@Simon мені особисто вважає, що це непряме лікування, "ми втратимо конкуренцію C #, якщо ми продовжимо дотримуватися попередніх Java5 способів вирішення подібних міркувань". :) Для прикладу "способів до Java5" див. JDK-1223179: перемикання рядків - подано в 1995 році і швидко закривається, як не виправиться : "Не затримуй дихання. Нічого, що нагадує це, у наших планах . "
гнат

Дякую, дуже цікаво побачити причини, висунуті у пропозиції. Все ще невірно сказати, що ми вводимо тип у програму "без поважних причин" - ми програмісти ОО! :)
черговий день

5
@anotherdave "об'єктно-орієнтована" виправдовує введення типів лише тоді, коли вони служать значущому призначенню. Мабуть, переважаюча думка в JCP виявилася, що використання переписок як безмозкових обгортків для String констант в комутаторі не кваліфікується як значуще
gnat

1
Нічого собі, я здивований (хорошим способом), коли бачу офіційну пропозицію, яка активно протидіє роздуванню типу. Це освіжає. Тип роздуття є величезною проблемою на Яві.
Бен Лі

7

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

Якщо ви порівнюєте більше, скажімо, трьох струн, то switch, ймовірно, більш читабельний і компактний. Це, ймовірно, буде краще, хоча ви навряд чи помітите різницю, якщо не будете робити велику кількість порівнянь на гарячому шляху коду.


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

Але тоді Map у поєднанні з командним шаблоном навіть принесе більше читабельності з однаковою складністю, оскільки у вас є додатковий тип команди
SpaceTrucker

3

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

Ще одна примітна річ у JDK 7 switch- це те, що він набагато більш сприятливий, ніж if-elseконструкції.

І приємним випадком використання для швидких switches рядків може бути JSON / XML потік розбору, коли вам потрібно зробити багато комутаторів на типи вузлів і атрибутів. Я не можу придумати кращого варіанту для цього.


1
"Перемикання для рядків є незамінним, коли ваші значення перерахунку надходять ззовні, тобто зберігаються в базі даних.": Чи можете ви детальніше розглянути це? Рядки в операторі перемикання жорстко закодовані: як тільки рядки в базі даних будуть змінені, ваш код застарів.
Джорджіо

Ну, ви праві - enums можна використовувати в цьому випадку через статичну природу Java. Коли я писав це, я думав про Roleбібліотеку безпеки, яка зазвичай надається як клас і не може бути введена в неї enum. Інший випадок - динамічно складений код Java / Groovy - в цій ситуації, яка є рідкісною, струнні комутатори можуть бути кращим варіантом.
Андрій Чащев

2

Простота

Підтримка String in Switch корисна для обробки даних без перетворення в enum або if-elseлогіку. Іноді просто увімкнути String.

З розсилки пропозицій у списку розсилки JDK 7 (проект Coin) ( @gnat відповідь )

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

Версія 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 (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");
}

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);

Мені незрозуміло на прикладі коду та ваших коментарів вище. Ви маєте на увазі Енума як приклад чогось важкого для читання?
черговий день


2
Це дуже надумано; ти робиш вигляд, що в enum не було б поля електронної пошти чи що це не клас.
Дейв Ньютон

4
@ user2860598 Якщо ви намагаєтеся зробити точку зору, використовуйте відповідний приклад. Він також не стосується жирового перебору струнної константи.
Дейв Ньютон

1
@ user2860598 Відповідь, яку ви пов’язали, говорить про те, що там було введено & що раніше вони могли бути "апроксиміровані" за допомогою Enum. Моя думка полягала в тому, що використання Enum все ще є переважним, навіть з їх впровадженням.
черговий день

0

Енуми великі, і ви повинні використовувати їх замість Strings, коли у вас є можливість. Але бувають випадки, коли ви просто не могли, наприклад, коли вам потрібно працювати з зовнішнім об’єктом, що надходить з-за меж Java. Уявіть, що вам потрібно щось розібрати. Як відповідь сервера, конфігурація, файл журналу чи щось подібне: у вас є купа варіантів, які ви шукаєте, і немає ніякого способу, щоб вони могли стати перерахунками. Тож у Java <7 ви б застрягли з купою

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

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

TLDR : ви абсолютно повинні використовувати Enums, коли ви працюєте виключно з кодом Java, але це не завжди можливо із зовнішніми об'єктами. Сподіваюся, моє пояснення має сенс.


Якщо ви маєте справу з зовнішніми даними, і ви вже знаєте достатньо , щоб знати , що вам потрібно в ваших ifзаявах, то ви повинні бути обертати його в будь-якому випадку , що означає , що ви могли використовувати перерахування або шаблон команди і т.д.
Дейв Ньютон

@DaveNewton так, ви все одно можете використовувати переписки, але, як я вже зазначив у своїй відповіді, іноді це не практично, як у випадку, коли ви в кінцевому підсумку створюєте завислими переліки, які використовуються лише один раз у вашому коді під час розбору.

@dimoniy Але якщо вам довелося створити 100s значень Enum, чи не означатиме це, що для альтернативи перемикання вам доведеться мати 100-ти випадок справ? Якщо існує стільки змінних, я б напевно віддав перевагу безпеці переліків.
черговий день

0

Я не згоден з думкою, що це більш чистий і читабельний код, коли ви використовуєте Stringsв операторах переключення, і вважаю, що це погана практика програмування. Якщо ви зміните значення, яке ви використовуєте для зберігання, вам доведеться його змінювати кожен перемикач або виникнення if-elseif. Це не добре, оскільки ви вводите твердо кодовані значення кожній бірці свого коду. Що ти збираєшся робити, якщо одного разу ти вирішиш змінити одне з цих твердо кодованих значень? Шукати та замінювати кожну його копію?

Якщо у вас є якісь твердо кодовані значення, які ви робите, якщо виконуються операції if-elseif, використання постійних примітивних значень для них набагато краще в порівнянні з Java 1.5 лише тому, що це забезпечує безпеку типу.

Гаразд, будуть ситуації, коли ви отримаєте значення String (з HTTP Request, файлів тощо), і перетворення їх у примітивні значення - це біль. Але Enumв цьому моменті зробіть гарну роботу. Тому що, коли ви хочете змінити значення, яке ви зберігаєте в Enum, просто змінюйте його, коли ви заявляєте. У коді нічого іншого не потрібно змінювати. (Звичайно, потрібно змінити значення, які зберігаються десь в іншому місці).

Якщо ви просто впроваджуєте брудний і ледачий код, це ідеально. Ви не хочете залучати до коду безліч Enums, але у складному масштабному програмному забезпеченні, яке вб'є вас.


-1

Якщо ви знаєте, яка інформація надходить і що вона може бути лише з 5 штатів, тоді використовуйте Enum. Однак якщо можливих станів може бути більше 9000, і вам потрібно лише знайти 42, то використовувати перемикач краще, тому що ви не хочете вводити всі ці стани.

Енум, як правило, є вибором у більшості випадків, крім випадків, коли можливі стани невідомі або їх багато, і вас хвилює лише декілька.

Але чому вони його запровадили зараз? Це була лише зміна, яка дозволила отримати чистіший код.

У 1.5 вони також дозволили вам створити власні тіла для Enums.


Але ви не просто присвоїли б інші 8958 можливих значень одному стану Enum?
черговий день

@anotherdave Це де defaultв switchприходить до гри. Я не знаю, що у вас може бути стан за замовчуванням Enum, якщо ви не зробите стан за замовчуванням, але тоді цей код просто роздувається, тоді як A switchнабагато чистіше використовувати.

3
Дивно говорити, що UNKNOWNстан Енума роздуто, але defaultсправа на комутаторі - ні.
черговий день
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.