Для чого нам потрібна перерва після заяв про справи?


94

Чому компілятор автоматично не ставить оператори розриву після кожного блоку коду в комутаторі? Це з історичних причин? Коли ви хочете виконати кілька блоків коду?


2
Зробив відповідь про JDK-12 і перемкнув мітки, переформовані таким чином, щоб вони не мали повноважень break.
Наман

Відповіді:


94

Іноді корисно мати кілька випадків, пов’язаних з одним блоком коду, наприклад

case 'A':
case 'B':
case 'C':
    doSomething();
    break;

case 'D':
case 'E':
    doSomethingElse();
    break;

і т. д. Просто приклад.

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


28
Просто завжди додайте коментар у рядку, // Intentional fallthrough.коли упустите перерву. На мою думку, це не стільки поганий стиль, скільки "легко забути перерву". PS Звичайно, не в простих випадках, як у самій відповіді.
подвоєне

@doublep - я згоден. На мій погляд, я б уникав цього, якщо це можливо, але якщо це має сенс, то переконайтеся, що вам дуже ясно, що ви робите.
WildCrustacean

6
@doublep: Я б не турбувався з коментарем, якщо таким caseчином зібрано кілька s. Якщо між ними є код, то так, коментар, ймовірно, заслуговує.
Біллі ONeal

4
Я уявляю мову, де можна оголосити декілька випадків у межах одного case, так: case 'A','B','C': doSomething(); case 'D','E': doSomethingElse();без необхідності перерви між справами. Паскаль міг це зробити: "Оператор case порівнює значення порядкового виразу з кожним селектором, який може бути константою, піддіапазоном або їх списком, розділеним комами". ( wiki.freepascal.org/Case )
Крістіан Семрау

32

Історично склався так , що це тому , що caseпо суті , визначає напрямок label, також відоме як цільова точка у вигляді gotoвиклику. Оператор перемикання та пов'язані з ним випадки справді просто представляють багатогалузеву гілку з декількома потенційними точками входу в потік коду.

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


30

Java походить від C, і це синтаксис від C.

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

class SwitchDemo2 {
    public static void main(String[] args) {

        int month = 2;
        int year = 2000;
        int numDays = 0;

        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                numDays = 31;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                numDays = 30;
                break;
            case 2:
                if ( ((year % 4 == 0) && !(year % 100 == 0))
                     || (year % 400 == 0) )
                    numDays = 29;
                else
                    numDays = 28;
                break;
            default:
                System.out.println("Invalid month.");
                break;
        }
        System.out.println("Number of Days = " + numDays);
    }
}

4
Хтось націлився на стрілку вгору і пропустив? А може, у них була яловичина з вашим фігурним стилем або вдавленням ...
Джим Льюїс,

Не знаю, так що отримайте +1 від мене. Це приклад, коли допомога допомагає, хоча я справді бажаю, щоб Java обрала більш сучасний виклад справи. EVALUATE-WHEN-OTHERWISE від Cobol набагато потужніший і передує Java. Вираз відповідності Scala - сучасний приклад того, що можна зробити.
Джим Ферранс

1
Мої студенти будуть публічно розбиті за це. Це койот негарний.
ncmathsadist

2
@ncmathsadist Це ілюструє суть одного способу щось зробити. Я не погоджуюся, що цей приклад, ймовірно, крайній. Але це приклад із реального світу, який, на мою думку, допомагає людям зрозуміти концепцію.
Ромен Гіппо

15

Я думаю, що це помилка. Як мовна конструкція її так само легко мати, breakяк і за замовчуванням, і натомість мати fallthroughключове слово. Більшість коду, який я написав і прочитав, має перерву після кожного випадку.


4
Я б краще запропонував, continue <case name>що дозволяє чітко вказати, з якою справою продовжувати;
Вількс–

4
@Vilx При дозволі довільного caseв межах поточного switch, це просто стає goto. ;-)
Крістіан Семрау,

13

Ви можете робити всілякі цікаві речі із випадками пропускання корпусу.

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

switch (someValue)
{
    case extendedActionValue:
        // do extended action here, falls through to normal action
    case normalActionValue:
    case otherNormalActionValue:
        // do normal action here
        break;
}

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


Чи можна перемикач / регістр використовувати на рядках у Java?
Стів Куо,

@ Steve: На жаль, наразі ні. Відповідно до stackoverflow.com/questions/338206/… , рядки будуть дозволені в майбутній версії Java. (В даний час більшість моїх програм програмую в C #, що дозволяє робити рядки в операторах переключення.) Я відредагував відповідь, щоб видалити оманливі лапки.
Зак Джонсон,

2
@ZachJohnson, набагато пізніше Java 7 дійсно дозволяє вмикати рядки.
Боб Крос

7

Чому компілятор автоматично не ставить оператори розриву після кожного блоку коду в комутаторі?

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

Це з історичних причин? Коли ви хочете виконати кілька блоків коду?

Це, головним чином, для сумісності з C, і, мабуть, давній хакер із старих часів, коли gotoключові слова блукали по землі. Це , звичайно, дає змогу робити деякі дивовижні речі, наприклад , пристрій Даффа , але чи то це на його користь чи проти - це… аргументація в кращому випадку.


5

breakПісля того, як перемикач cases використовується , щоб уникнути провалюється в звітності комутатора. Хоча цікаво, цього зараз можна досягти за допомогою новостворених міток комутаторів, реалізованих за допомогою JEP-325 .

За допомогою цих змін можна уникнути breakкожного перемикача, caseяк показано далі: -

public class SwitchExpressionsNoFallThrough {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int value = scanner.nextInt();
        /*
         * Before JEP-325
         */
        switch (value) {
            case 1:
                System.out.println("one");
            case 2:
                System.out.println("two");
            default:
                System.out.println("many");
        }

        /*
         * After JEP-325
         */
        switch (value) {
            case 1 ->System.out.println("one");
            case 2 ->System.out.println("two");
            default ->System.out.println("many");
        }
    }
}

На виконання вищевказаного коду з JDK-12 , то порівняльний вихід можна розглядати як

//input
1
// output from the implementation before JEP-325
one
two
many
// output from the implementation after JEP-325
one

і

//input
2
// output from the implementation before JEP-325
two
many
// output from the implementation after JEP-325
two

і звичайно річ незмінна

// input
3
many // default case match
many // branches to 'default' as well

4

Тому вам не доведеться повторювати код, якщо вам потрібно кілька випадків, щоб зробити одне і те ж:

case THIS:
case THAT:
{
    code;
    break;
}

Або ви можете робити такі речі:

case THIS:
{
   do this;
}
case THAT:
{
   do that;
}

По-каскадному.

Дійсно схильність до помилок / плутанини, якщо ви запитаєте мене.


це працює як для цього, так do thisі do thatдля цього, але тільки do thatдля цього?
JonnyRaa

1
просто читайте документи. Це жахливо! Який простий спосіб писати помилки!
JonnyRaa

4

Що стосується історичних записів, Тоні Хоаре вигадав випадок у 1960-х роках, під час революції "структурованого програмування". Заява про справу Тоні підтримує декілька міток на кожний випадок і автоматичний вихід без смердючих breakтверджень. Вимога до явного breakполягала в тому, що виходило з лінії BCPL / B / C. Денніс Річі пише (в ACM HOPL-II):

Наприклад, завершення, яке виходить з оператора переключення BCPL, не було в мові, коли ми дізналися його в 1960-х роках, і тому перевантаження ключового слова перерви для виходу з оператора вимикача B і C пов'язане з еволюційною еволюцією, а не усвідомленою змінити.

Мені не вдалося знайти жодних історичних творів про BCPL, але коментар Рітчі говорить про те, що це breakбула більш-менш історична аварія. Пізніше BCPL вирішив проблему, але, можливо, Рітчі та Томпсон були надто зайняті винаходом Unix, щоб його непокоїти така деталь :-)


Це має набрати більше голосів. Очевидно, що ОП вже знав, що пропуск breakдозволяє "виконувати кілька блоків коду", і більше стосується мотивації цього вибору дизайну. Інші згадували про відому спадщину від С до Яви, і ця відповідь підштовхнула дослідження ще далі до днів до С. Я хотів би, щоб у нас був такий (хоча і дуже примітивний) шаблон, який відповідає з самого початку.
wlnirvana

3

Java походить від мови C, спадщина якої включає техніку, відому як Пристрій Даффа . Це оптимізація, яка спирається на той факт, що контроль падає від одного випадку до іншого, за відсутності break;заяви. До того моменту, коли С був стандартизований, коду такого типу "в природі" було багато, і змінити мову, щоб порушити такі конструкції, було б контрпродуктивно.


1

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

public class SwitchTester{
    private static final Log log = LogFactory.getLog(SwitchTester.class);
    public static void main(String[] args){
        log.info(monthsOfTheSeason(Season.WINTER));
        log.info(monthsOfTheSeason(Season.SPRING));
        log.info(monthsOfTheSeason(Season.SUMMER));
        log.info(monthsOfTheSeason(Season.AUTUMN));
    }

    enum Season{WINTER, SPRING, SUMMER, AUTUMN};

    static String monthsOfTheSeason(Season season){
        switch(season){
            case WINTER:
                return "Dec, Jan, Feb";
            case SPRING:
                return "Mar, Apr, May";
            case SUMMER:
                return "Jun, Jul, Aug";
            case AUTUMN:
                return "Sep, Oct, Nov";
            default:
                //actually a NullPointerException will be thrown before reaching this
                throw new IllegalArgumentException("Season must not be null");
        }        
    }
}   

Виконання друкує:

12:37:25.760 [main] INFO lang.SwitchTester - Dec, Jan, Feb
12:37:25.762 [main] INFO lang.SwitchTester - Mar, Apr, May
12:37:25.762 [main] INFO lang.SwitchTester - Jun, Jul, Aug
12:37:25.762 [main] INFO lang.SwitchTester - Sep, Oct, Nov

як і очікувалося.


0

Відсутність автоматичного розриву, доданого компілятором, дозволяє використовувати перемикач / корпус для перевірки таких умов, як 1 <= a <= 3видалення оператора розриву з 1 і 2.

switch(a) {
  case 1: //I'm between 1 and 3
  case 2: //I'm between 1 and 3
  case 3: //I'm between 1 and 3
          break;
}

Йєч. Я цілком ненавиджу це.
ncmathsadist

0

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


0

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

наприклад, використання кодів відповіді http для автентифікації користувача за допомогою маркера часу

код відповіді сервера 401 - маркер застарілий -> регенерувати маркер і ввійти в систему.
Код відповіді сервера 200 - маркер нормально -> зареєструватися.

у випадках заяви:

case 404:
case 500:
        {
            Log.v("Server responses","Unable to respond due to server error");
            break;
        }
        case 401:
        {
             //regenerate token
        }
        case 200:
        {
            // log in user
            break;
        }

Використовуючи це, вам не потрібно викликати функцію входу користувача для відповіді 401, оскільки коли регенерується маркер, час виконання переходить у корпус 200.


0

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

public static void spanishNumbers(String span){

    span = span.toLowerCase().replace(" ", "");
    switch (span){
     case "1":    
     case "jan":  System.out.println("uno"); break;    
     case "2":      
     case "feb":  System.out.println("dos"); break;    
     case "3":     
     case "mar":  System.out.println("tres"); break;   
     case "4":   
     case "apr":  System.out.println("cuatro"); break;
     case "5":    
     case "may":  System.out.println("cinco"); break;
     case "6":     
     case "jun":  System.out.println("seis"); break;
     case "7":    
     case "jul":  System.out.println("seite"); break;
     case "8":    
     case "aug":  System.out.println("ocho"); break;
     case "9":   
     case "sep":  System.out.println("nueve"); break;
     case "10":    
     case "oct": System.out.println("diez"); break;
     }
 }

0

Зараз я працюю над проектом, де мені це потрібно breakв моєму операторі перемикача, інакше код не буде працювати. Потерпіть зі мною, і я дам вам хороший приклад того, чому вам це потрібно, breakу вашій заяві про перемикання.

Уявіть, у вас є три стани: один, який чекає, поки користувач введе число, другий для його обчислення і третій для друку суми.

У такому випадку у вас є:

  1. Стан1 - Зачекайте, коли користувач введе номер
  2. Стан2 - Надрукуйте суму
  3. стан3 - Обчисліть суму

Дивлячись на держави, ви хотіли б , щоб порядок поборів , щоб почати на State1 , потім state3 і , нарешті , State2 . В іншому випадку ми будемо друкувати лише дані користувачів без розрахунку суми. Щоб ще раз це пояснити, ми чекаємо, поки користувач введе значення, потім обчислюємо суму і друкуємо суму.

Ось приклад коду:

while(1){
    switch(state){
      case state1:
        // Wait for user input code
        state = state3; // Jump to state3
        break;
      case state2:
        //Print the sum code
        state = state3; // Jump to state3;
      case state3:
        // Calculate the sum code
        state = wait; // Jump to state1
        break;
    }
}

Якщо ми не використовуємо break, то він буде виконуватися в цьому порядку, State1 , состояніе2 і state3 . Але, використовуючи break, ми уникаємо цього сценарію і можемо замовити в правильній процедурі, яка повинна починатися з state1, then state3 і останнього, але не менш важливого стану2.


-1

Саме тому, що за допомогою розумного розміщення ви можете виконувати блоки в каскаді.

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