Изявление за превключване на Java

1. Общ преглед

В този урок ще научим какво представлява операторът за превключване и как да го използваме.

Операторът switch ни позволява да заменим няколко вложени конструкции if-else и по този начин да подобрим четливостта на нашия код.

Switch еволюира с течение на времето - добавени са нови поддържани типове, особено в Java 5 и 7. Също така, той продължава да се развива - изразите за превключване вероятно ще бъдат въведени в Java 12.

По-долу ще дам някои кодови примери, които демонстрират използването на превключвател изявление, ролята на почивка изявление, изискванията за превключване аргумент / случай стойностите и сравнението на String е в превключвател изявление.

Нека да преминем към примера.

2. Пример за употреба

Да приемем, че имаме следните вложени оператори if-else :

public String exampleOfIF(String animal) { String result; if (animal.equals("DOG") || animal.equals("CAT")) { result = "domestic animal"; } else if (animal.equals("TIGER")) { result = "wild animal"; } else { result = "unknown animal"; } return result; }

Горният код не изглежда добре и би било трудно да се поддържа и да се разсъждава. За да подобрим четливостта, бихме могли да използваме оператор за превключване тук:

public String exampleOfSwitch(String animal) { String result; switch (animal) { case "DOG": result = "domestic animal"; break; case "CAT": result = "domestic animal"; break; case "TIGER": result = "wild animal"; break; default: result = "unknown animal"; break; } return result; }

Както е показано по-горе, ние сравняваме аргумента за превключване на животното с няколкото стойности на регистъра . Ако нито една от стойностите на регистъра не е равна на аргумента, тогава се изпълнява блока под етикета по подразбиране .

Най-просто казано, операторът break се използва за излизане от оператор за превключване .

3. прекъсване Отчета

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

Ако забравим да напишем почивка , блоковете отдолу ще бъдат изпълнени.

За да демонстрираме това, нека пропуснем операторите break и добавим изхода към конзолата за всеки блок:

public String forgetBreakInSwitch(String animal) { switch (animal) { case "DOG": System.out.println("domestic animal"); default: System.out.println("unknown animal"); } }

Нека изпълним този код, забравямеБийнСвитч ( „КУЧЕ”) и проверяваме изхода, за да докажем, че всички блокове се изпълняват:

domestic animal unknown animal

Затова трябва да бъдем внимателни и да добавяме инструкции за прекъсване в края на всеки блок, освен ако няма нужда да преминем към кода под следващия етикет.

Единственият блок, при който прекъсването не е необходимо, е последният, но добавянето на прекъсване към последния блок прави кода по-малко податлив на грешки.

Също така можем да се възползваме от това поведение, за да пропуснем break, когато искаме да се изпълни един и същ код за няколко оператора case. Нека препишем примера в предишния раздел, като групираме първите 2 случая:

public String exampleOfSwitch(String animal) { String result; switch (animal) { case "DOG": case "CAT": result = "domestic animal"; break; case "TIGER": result = "wild animal"; break; default: result = "unknown animal"; break; } return result; }

4. превключване на аргумент и стойности на регистъра

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

4.1. Типове данни

Не можем да сравним всички видове обекти и примитиви в оператора за превключване . А превключвател работи само с четири примитиви и техните опаковки, както и с вида ENUM и String класа:

  • байт и байт
  • кратко и кратко
  • int и Integer
  • char и Характер
  • преброяване
  • Струна

Типът низ е наличен в оператора за превключване , започващ с Java 7.

типът enum е въведен в Java 5 и оттогава е наличен в оператора за превключване .

Класовете на обвивки също са на разположение от Java 5.

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

4.2. Няма нулеви стойности

Не можем да предадем нулевата стойност като аргумент на оператор за превключване . Ако го направим, програмата ще изхвърли NullPointerException, използвайки нашия първи пример за превключване :

@Test(expected=NullPointerException.class) public void whenSwitchAgumentIsNull_thenNullPointerException() { String animal = null; Assert.assertEquals("domestic animal", s.exampleOfSwitch(animal)); }

Разбира се, не можем също така да предадем null като стойност на етикета за случай на оператор за превключване . Ако го направим, кодът няма да се компилира.

4.3. Стойности на делата като константи на времето за компилация

Ако се опитаме да заменим стойността на случая DOG с променливата dog, кодът няма да се компилира, докато не маркираме променливата dog като окончателна :

final String dog="DOG"; String cat="CAT"; switch (animal) { case dog: //compiles result = "domestic animal"; case cat: //does not compile result = "feline" }

4.4. Сравнение на низове

Ако оператор за превключване използва оператора за равенство за сравняване на низове, не можем да сравним аргумент String , създаден с новия оператор, със стойност на случай на String .

За щастие операторът на превключвател използва метода equals () под капака .

Нека да демонстрираме това:

@Test public void whenCompareStrings_thenByEqual() { String animal = new String("DOG"); assertEquals("domestic animal", s.exampleOfSwitch(animal)); }

5. превключване на изрази

JDK 13 вече е наличен и предлага подобрена версия на нова функция, представена за първи път в JDK 12: изразът на превключвателя .

За да го активираме, трябва да предадем –enable-preview на компилатора.

5.1. Новият превключвател Expression

Да видим какво новите превключвател израз изглежда като при превключване месеца:

var result = switch(month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1; case MARCH, MAY, APRIL, AUGUST -> 2; default -> 0; }; 

Изпращането на стойност като Month.JUNE би задало резултата на 3.

Notice that the new syntax uses the ->operator instead of the colon we're used to with switch statements. Also, there's no break keyword: The switch expression doesn't fall through cases.

Another addition is the fact that we can now have comma-delimited criteria.

5.2. The yield Keyword

Going a bit further, there's a possibility to obtain fine-grained control over what's happening on the right side of the expression by using code blocks. In such a case, we need to use the keyword yield:

var result = switch (month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1; case MARCH, MAY, APRIL, AUGUST -> { int monthLength = month.toString().length(); yield monthLength * 4; } default -> 0; };

While our example is a bit arbitrary, the point here is that we've got access to more of the Java language here.

5.3. Returning Inside switch Expressions

As a consequence of the distinction between switch statements and switch expressions, it is possible to return from inside a switch statement, but we're not allowed to do so from within a switch expression.

The following example is perfectly valid and will compile:

switch (month) { case JANUARY, JUNE, JULY -> { return 3; } default -> { return 0; } }

However, the following code will not compile, as we are trying to return outside of an enclosing switch expression:

var result = switch (month) { case JANUARY, JUNE, JULY -> { return 3; } default -> { return 0; } };

5.4. Exhaustiveness

When using switch statements, it doesn't really matter if all cases are covered.

The following code, for example, is perfectly valid and will compile:

switch (month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER -> 1; }

For switch expressions though, the compiler insists that all possible cases are covered. The following code snippet, for example, would not compile, as there's no default case and not all possible cases are covered:

var result = switch (month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER -> 1; }

The switch expression, however, will be valid when all possible cases are covered, like in the following example:

var result = switch (month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1; case MARCH, MAY, APRIL, AUGUST -> 2; }

Please note that the above code snippet does not have a default case. As long as all cases are covered, the switch expression will be valid.

6. Conclusion

В този урок научихме тънкостите при използването на инструкцията за превключване в Java. Можем да решим дали да използваме превключвател въз основа на четливостта и вида на сравняваните стойности.

Извлечението за превключване е добър кандидат за случаи, когато имаме ограничен брой опции в предварително дефиниран набор (например: дни от седмицата). В противен случай ще трябва да модифицираме кода всеки път, когато се добавя или премахва нова стойност, което може да не е осъществимо. За тези случаи трябва да разгледаме други подходи като полиморфизъм или други дизайнерски модели като Command.

Както винаги, пълният код на JDK 8 и кодът JDK 13 е наличен в GitHub.