Чому тип даних оператора перемикання не може бути довгим, Java?


80

Ось уривок із підручників Java із Sun :

Комутатор працює з byte, short, charі intпримітивними типами даних. Вона також працює з перерахованими типами (обговорювалися в класах і успадкування) і кілька спеціальних класів, «загорнути» деякі примітивні типи: Character, Byte, Short, і Integer(обговорюваний в об'єкти Simple Data).

Повинна бути поважна причина, чому longпримітивний тип даних заборонено. Хтось знає, що це?

Відповіді:


52

Я думаю, що певною мірою це було довільне рішення, засноване на типовому використанні комутатора.

По суті, комутатор може бути реалізований двома способами (або, в принципі, комбінацією): для невеликої кількості випадків або тих, значення яких широко розподілені, комутатор, по суті, стає еквівалентом серії ifs для тимчасової змінної ( значення, яке вмикається, має бути оцінене лише один раз). Для помірної кількості випадків, які є більш-менш послідовними за значенням, використовується таблиця перемикачів (інструкція TABLESWITCH на Java), завдяки якій місце для переходу ефективно шукається в таблиці.

Кожен із цих методів в принципі міг використовувати довге значення, а не ціле число. Але я думаю, що це було, мабуть, просто практичне рішення, щоб збалансувати складність набору інструкцій та компілятора з реальною потребою: випадки, коли вам дійсно потрібно перейти на довгий час, є досить рідкісними, щоб прийнятно було переписати як серія операторів IF або обробляти якимось іншим способом (якщо довгі значення, про які йдеться, знаходяться близько один до одного, ви можете у своєму коді Java переключити результат int віднімання найменшого значення).


Я повинен погодитися з аргументом "рідкість", оскільки я деякий час розробляв Java, і я ніколи не стикався з ситуацією, коли мені потрібно було / намагались увімкнути її до цього часу.
Фоста,

3
Димитріс, моє міркування не базувалося на розумінні безпеки потоків, просто я не думаю, що аргумент безпеки потоку, який ви висунули, має місце.
Ніл Коффі

13
Найдурніше дизайнерське рішення коли-небудь. У мене довгий, який є купа прапорів, і я повинен носити з if ... else if ... else ... Зовсім смішно.
m0skit0

2
Я згоден w / @ m0skit0. У моєму випадку хтось інший написав API, який використовує longs як константи, тому що це те, що вони зберігають у БД. Зараз я не можу використовувати комутатор / корпус через погане рішення когось іншого.
CodeChimp

1
Я не кажу, що це "кінець світу". Так, так, є способи обійти це, але всі вони здаються хаками. Моя думка полягає в тому, що, як розробники, ми завжди повинні намагатися думати минулим, як ми думаємо, що люди будуть використовувати те, що ми будуємо. У цьому випадку хтось прийняв рішення не дозволяти перемикання / справу на тривалий час, виходячи з початкової думки, що "Хто чесно мав би 2 ^ 64 справи". Можливо, це надто спрощення. Можливо, є якась інша причина, чому довготу не можна змінити, тому що ми просто теж не були таємними. Мені просто здається дивним не підтримувати це.
CodeChimp

21

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

[РЕДАГУВАТИ: Витягнуто з коментарів до цієї відповіді, з деякими доповненнями на задньому плані]

Якщо бути точним, 2³² - це багато випадків, і будь-яка програма з методом, достатньо довгим, щоб вмістити більше, ніж це, буде надзвичайно жахливою! Будь-якою мовою. (Найдовша функція, яку я знаю в будь-якому коді будь-якою мовою, - це трохи більше 6k SLOC - так, вона велика switch- і насправді некерована.) Якщо ви справді застрягли в тому, longде ви повинні мати лише intабо менше, тоді у вас є дві реальні альтернативи.

  1. Використовуйте якийсь варіант на тему хеш-функцій, щоб стиснути longфайл до int. Найпростіший, лише для використання, коли ви неправильно помилився з типом, - це просто кастинг! Більш корисним було б зробити це:

    (int) ((x&0xFFFFFFFF) ^ ((x >>> 32) & 0xFFFFFFFF))
    

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

  2. Набагато кращим рішенням, якщо ви працюєте з дуже великою кількістю випадків, є зміна дизайну на використання Map<Long,Runnable>або чогось подібного, щоб ви шукали, як відправити певне значення. Це дозволяє розділити справи на декілька файлів, що набагато простіше управляти, коли кількість випадків стає більшою, хоча це ускладнює організацію реєстрації хосту задіяних класів реалізації (анотації можуть допомогти, дозволяючи вам автоматично створювати реєстраційний код).

    FWIW, я зробив це багато років тому (ми перейшли на нещодавно випущений J2SE 1.2 через проект), будуючи власний механізм байт-коду для моделювання масивно паралельного обладнання (ні, повторне використання JVM не було б придатним через радикально різні моделі значення та виконання), і це надзвичайно спростило код щодо великого, switchщо використовувалася у версії коду C.

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


1
Але чи перемикається діапазон насправді більше 32 біт в ширину? Я ніколи не чув про код, який потребував такої кількості перемикачів зброї. Без цього ви можете ущільнити діапазон (наприклад, використовуючи деякі варіанти теми хеш-функцій), щоб зробити щось, що буде працювати. Або використовуйте a, Map<Long,Runnable>щоб вирішити проблему зовсім по-іншому. :-)
Donal Fellows

3
@ Лорд Торгамус: Імовірно, тому, що це безглуздо. Подумайте на мить: чому б у кого-небудь, у кого-небудь був код з понад 2 32 руками switch? Бажання вибрати з кінцевого набору з того, що багато елементів просто вказує на помилку у фундаментальному дизайні програми. Те, що люди просять про це, означає лише те, що ** вони неправильно розробили свій дизайн .
Donal Fellows

1
ДО ВСЬОГО, якщо хтось хоче сперечатися зі мною далі щодо цього, будь ласка, почніть із наведення варіанту використання для довгого включення. В іншому випадку ми застрягнемо сперечатися з гіпотетиками назавжди ...
Donal Fellows

2
@DonalFellows, ще один випадок - це Android з ListView. Хоча listViews може мати longкількість рядків, мені конкретно потрібно лише 4. Це випадок, коли мені longвручають, який вказує, на який рядок діє, і мені потрібно робити різні речі, виходячи з того, який це рядок. Думаю, я міг би перейти на int, але це полегшило б мені життя, якби я міг просто switchредагувати змінну. В даний час я просто використовую рядок ifі else ifзамість цього.
Крістіан Сміт

7
"Щоб повторити повідомлення про прийняття додому, бажання довго вмикати - це ознака того, що ви неправильно помилились у вашій програмі" - Ні, це не так! Наявність longзнака не означає, що ви збираєтеся перевірити всі можливості, так само, як наявність значка intабо a String, не означає, що це теж. Це означає, що у вас є величини, яких може бути декілька, мають великий діапазон . Ви можете перевірити кілька простих випадків і вникнути в defaultрешту. Потрібно робити зміни і касти означає, що ви ризикуєте втратити дані. Підсумок: це неправильне рішення щодо дизайну Java, а не проблема користувача.
jbx

6

Оскільки індекс таблиці пошуку повинен складати 32 біти.


3
Але знову ж таки switchне потрібно обов'язково реалізовувати таблицю пошуку.
Йоахім Зауер,

10
Якби це було так, вони ніколи не могли реалізувати перемикач для рядків (як вони наразі планують).
Dimitris Andreou

1
@DimitrisAndreou так, тепер ми можемо переключитися на рядки: D (протягом багатьох років: P)
J. Doe

-11

Довгий, в 32-бітній архітектурі, представлений двома словами . А тепер уявіть, що може статися, якщо через недостатню синхронізацію виконання оператора перемикання спостерігає long з його високими 32 бітами від однієї записи, а 32 низькими - від іншої! Можна спробувати поїхати до .... хтозна куди! В основному десь навмання. Навіть якби обидва записи представляли дійсні випадки для оператора switch, їх смішна комбінація, мабуть, не призвела б ні до першого, ні до другого - або вкрай гірше, це могло б призвести до іншого дійсного, але не пов’язаного між собою випадку!

Принаймні з int (або меншими типами), незалежно від того, наскільки сильно ви зіпсуєте, оператор switch буде принаймні читати значення, яке хтось насправді написав , замість значення "з повітря".

Звичайно, я не знаю фактичної причини (минуло більше 15 років, я так довго не звертав уваги!), Але якщо ви зрозумієте, наскільки небезпечною та непередбачуваною може бути така конструкція, погодьтеся, що це, безумовно, дуже вагома причина, щоб ніколи не вмикати longs (і як передбачалося long -pun - буде 32-бітна машина, ця причина залишатиметься чинною).


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

Ніл, твій аргумент досить заплутаний: "Але тоді, як тільки цей результат потрапить до реєстру або до стеку, до нього звертається лише потік перемикання і є безпечним". Звичайно, використання цього значення безпечно для потоку! Але моя суть у тому, що це значення може вже бути неправильним через помилки синхронізації в коді користувача . Використання помилково поточного значення менш корисно :) Цю проблему ніколи не можна усунути: паралельний код помилки вже міг створити значення "з повітря" / неправильне довге значення, яке згодом може бути використано в комутаторі, що перейти до адреси справи, яку ніхто ніколи не вказував .
Dimitris Andreou,

1
Дмітріс, можливо, щось у вашому аргументі є я не розумію. Ввімкнене значення дійсно може бути помилковим через помилки синхронізації в коді користувача. Але я не вірю, що в операторі switch є щось властиве, що робить це більш імовірним, ніж в інших випадках. І продумуючи це якнайкраще, я не вірю, що неатомність привіт / низьких слів довгих читань / записів у пам’ять насправді є проблемою. (Думаючи про речі по-іншому: ви могли б вирішити, що якщо порівняння довгих не дозволяється на основі того самого аргументу.)
Ніл Коффі

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

7
Я знаю, що це старе, і коментування цього начебто спірне, але я хочу підкреслити, що ваш аргумент стосується ifтакож і результату буде таким же поганим: неправильний результат ~> неправильна гілка взята. Створення довго if- else- ifланцюг замість того , щоб switchфактично призведе до точно такого ж результату.
Ніколай Парлог,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.