The switch statement
*FIGURE 1. The structure of a switch statement
Switch ifadələr ancaq aşağıdakı tipdə ola bilər:
int
vəInteger
byte
vəByte
short
vəShort
char
vəCharacter
String
(java 7`dən bəri)- enum values
float
, double
, boolean
və long
tipləri və həmçinin onların wrapper classları (Float
, Double
, Boolean
, Long
) switch ifadələr tərəfindən dəstəklənmir.
Mahiyyət baxımından bir iş görməsə belə aşağıdakı nümunədə verilmiş sintaksis səhv deyildir və normal compile və run olunur:
public static void main(String[] args) { switch (1) { default: break; } switch (2) { } }
Compile-time Constant Values
Hər bir case
ifadəsinin dəyəri switch
ifadəsinin dəyəri ilə eyni tipdə olan (əgər switch dəyişəni String tipindədirsə, case ifadəsinin də dəyəri String tipində olmalıdır və ya switch ifadə char tipindədirsə case dəyərlər 65535-dən yuxarı ola bilməz) compile-time constant dəyərlər olmalıdır. Bunlar ola bilər:
- literals;
- enum constants;
- final constant variables (final modifier ilə təyin olunmalı və elan olunduğu sətirdə literal dəyər mənimsədilməlidir; primitiv və String tiplər ola bilər, Wrapper classlar constant hesab edilmir).
Sonuncu bəndə əsasən wrapper classlar (Integer
, Byte
, Short
, Character
) switch ifadədə işlədilə bilər, amma case label kimi işlədilə bilməz. Suallarda buna diqqət etmək lazımdır.
Qısaca compile-time constant olması üçün dəyişən aşağıdakı şərtləri ödəməlidir:
- final olmalıdır;
- primitiv və ya String tipində olmalıdır;
- elan olunduğu sətirdə dəyər mənimsədilmiş olmalıdır;
- be assigned to a compile time constant expression.
Misal üçün private final int x = getX();
ifadəsində x
də həmçinin compile-time constant hesab edilmir. Başqa bir nümunəyə baxaq:
private int getSortOrder(String firstName, final String lastName) { String middleName = "MBM"; final String suffix = "MM"; int id = 0; switch(firstName) { case "Test": // String literal return 52; case suffix: // final constant variable id = 7; break; case middleName: // does not compile, because it is not final id = 5; break; case lastName: // does not compile, final but not constant id = 8; break; case 5: // does not compile, not String id = 5; break; case 'J': // does not compile, not String id = 10; break; case java.time.DayOfWeek.SUNDAY.toString(): //does not compile, enum id = 15; break; case "string".toUpperCase(): // does not compile break; case "string".toString(): // does not compile break; case "string".trim(): // does not compile break; } return id; }
Bir sıra incə məqamlara da nəzər salaq. Misal üçün aşağıdakı nümunə compile xətası verəcək, çünki char
birbaşa Integer
tipində olan dəyişənə mənimsədilə bilməz:
Integer x = 1; // int x = 1; is valid. switch (x) { case 'a': // does not compile System.out.println("a"); }
Bildiyimiz kimi char
dəyişəninə rəqəm (numeric) tipli dəyər mənimsədə bilərik. Bu məntiqlə yanaşsaq deyə bilərik ki, switch char
tipindədirsə case byte
tipində ola bilər. Amma bu hər zaman doğru deyildir, çünki byte
mənfi ədədlər də ala bilər, char
isə mənfi dəyər qəbul edə bilməz:
char c = 'a'; switch (c) { case -1: System.out.println("-1"); // Doesn’t compile: "possible loss of precision" }
Əgər case ifadədə -1
əvəzinə (char) -1
yazsaq compile olunacaq.
Həmçinin də switch ifadə əgər byte tipindədirsə, case dəyərlər -128 < case_label < 127 aralığında dəyər ala bilərlər.
byte b = 100; switch (b) { case 100: case 'a': default: case 'b': case 150: // does not compile case 200: // does not compile }
İmtahan üçün char dəyərlərin rəqəm qarşılığını bilmək tələb olunmur, amma ümumi olaraq yadda saxlaya bilərsiniz ki, ingilis əlifbasının bütün böyük və kiçik hərflərinin və eləcə də 0-9 aralığındakı rəqəmlərin hamısının rəqəm qarşılığı 127-dən kiçikdir. Yəni əgər switch ifadə byte tipindədirsə, case label olaraq qeyd etdiyimiz char dəyərlərin hər birini rahatlıqla istifadə edə bilərsiniz. ASCII kod siyahısına aşağıdakı linkdən baxa bilərsiniz:
Case dəyərlər təkrarlana bilməz, əks halda təkrarlanan sətir xəta verəcək:
switch (new Integer(2)) { case 1: System.out.println("1"); case 2: System.out.println("2"); case 3: System.out.println("3"); case 2: System.out.println("duplicate 2"); // does not compile case 1: System.out.println("duplicate 1"); // does not compile }
Nümunə bir az qarışıq formada da verilə bilər:
switch (0) { case 97: System.out.println("97"); case 16: System.out.println("2"); case (int)'b': System.out.println("98"); case (int)'a': System.out.println("duplicate 97"); // does not compile case 8*2: System.out.println("duplicate 16"); // does not compile }
Yaxşı olar ki, təsəvvürünüzə gələn bütün mümkün variantları yazıb özünüz üçün test edəsiniz.
Bir switch blokunda default
ifadəsi ancaq bir dəfə istifadə oluna bilər, əks halda kod xəta verəcək. default
ifadəsi switch`də istənilən yerdə yazıla bilər; əvvəldə, ortada, axırda, case ifadələri ilə yerləşmə ardıcıllığında heç bir məhdudiyyət yoxdur. Amma icra olunma ardıcıllığı fərqlidir, default əvvəldə və ya ortada yerləşməsindən asılı olmayaraq həmişə sonda icra olunur. Əvvəlcə case ifadələri bir-bir yoxlanılır və əgər heç bir uyğunluq olmasa o zaman default ifadəsi axtarılır və əgər varsa o zaman default icra olunur. Və ən vacib məqamlardan biri: əgər uyğunluq hansı sətirdə tapılsa, həmin sətirdən başlayaraq switch ifadəsinin sonuna kimi break
ifadəsi olmazsa bütün case və default ifadələrinə daxil olur.
int dayOfWeek = 5; switch(dayOfWeek) { case 0: System.out.print(" Sunday"); default: System.out.print(" Weekday"); case 6: System.out.println(" Saturday"); } // Output: Weekday Saturday
Başqa bir maraqlı nümunəyə baxaq:
public void getTypeOfDayWithSwitchStatement(String dayOfWeekArg) { String typeOfDay; switch (dayOfWeekArg) { case "Monday": typeOfDay = "Start of work week"; break; case "Tuesday": case "Wednesday": case "Thursday": typeOfDay = "Midweek"; break; case "Friday": typeOfDay = "End of work week"; break; case "Saturday": case "Sunday": typeOfDay = "Weekend"; break; default: throw new IllegalArgumentException("Invalid day of the week"); } System.out.println(typeOfDay); // line A }
Əgər bu metodda istənilən case ifadəsinin birinde typeOfDay
dəyişəninə dəyər mənimsədilməsini və ya default`da throw
ifadəsini commentə atsaq line A compile olunmayacaq. Çünki compiler yüz faiz əmin olmalıdır ki, istənilən hər bir situasiyada typeOfDay
dəyişəninə dəyər mənimsədiləcək, əks halda local dəyişən olduğu üçün onu çap edə bilməz.
Switch ifadələrə göndərilən String
obyektlərin case labela uyğun olub olmadığını yoxlamaq üçün Java equals()
metodundan istifadə edir. Ümumiyyətlə, əgər müqayisə ediləcək şərtlərin sayı çoxdursa, o zaman if-then-else ifadəsi ilə müqayisədə switch ifadəsinin istifadə edilməsi daha məqsədəuyğundur. Çünki switch ifadəsi həm if-then-else ifadəsinə nisbətən daha oxunaqlıdı, həm də performans baxımından daha yaxşıdır. Çünki dərləmə (compilation) zamanı switch üçün “jump table” yaradılır və artıq icra (execution) zamanı hansı case label`ın uyğun gəldiyi yoxlanılmır, sadəcə hansı case`in icra olunacağına qərar verilir. Əlavə məlumat üçün aşağıdakı linkə baxa bilərsiniz:
Bəzən switch sintaksislərində “case else” label`ına rast gələ bilərsiniz. Switch ifadələrində bu cür sintaksis mövcud deyil, if`dəki else
ilə bunu çaşdırmayın. “case else” əvəzinə “default” label istifadə olunur.
Switch ifadələrində enum da istifadə etmək mümkündür. Baxmayaraq ki, enum OCP imtahanı mövzularına daxildir, hər ehtimala qarşı tanış olmaqda fayda var. Təsəvvür üçün bir nümunəyə baxaq:
class TestEnumWithSwitch { public static void main(String[] args) { int i = 1; switch (Seasons.SUMMER) { default: i++; case WINTER: i+=1; case SPRING: i+=2; case SUMMER: i+=3; case AUTUMN: i+=4; } System.out.println(i); // output: 8 } } enum Seasons { WINTER, SPRING, SUMMER, AUTUMN }
Bəzi suallarda maksimum diqqətli olmaq lazımdır, “sağı göstərib sol ilə də vura bilərlər”. Ani bir diqqətsizlik üzündən çox sadə bir sualı səhv də cavablandıra bilərsiniz. Aşağıdakı nümunəyə baxaq:
int i = 8 / 3 - 1; switch(i){ case: 0 System.out.print("0"); break; case: 1 System.out.print("1"); break; default: System.out.println("default"); }
Bu testdə ilkin olaraq diqqət 4-cü sətirə yönəldilir. switch ifadə double
tipində dəyər qəbul edə bilməz, adətən bu prizmadan yanaşıb qərar qəbul edirik:
- ya 4-cü sətir compile olunmur;
- yaxud da 4-cü sətir compile olunur və i`nin dəyərinə əsasən hansı case label çap olunur onu təyin edirik.
Əslində isə səhv tamam başqa yerdədir: 6 və 7-ci sətirlərdə. case label`larda sintaksisə görə iki nöqtə case`in dəyərindən sonra yazılır, case`dən sonra yox. Bu səbəbdən kod compile olunmur.
Son olaraq switch ilə bağlı “Java sertifikat sualları” facebook qrupumuzda müzakirə edilmiş daha bir maraqlı sualı qeyd edirəm, cavabını özünüz təxmin etməyə çalışın:
class SwitchExample { public static void main(String[] args) { String str = null; switch (str) { case "null": System.out.println("1"); break; case "": System.out.println("2"); break; default: System.out.println("3"); } } }
Əgər təxminləriniz olmazsa, doğru cavab və izahı görmək üçün aşağıdakı linkə daxil ola bilərsiniz:
https://www.facebook.com/groups/javacertification/permalink/917643694951436/
[topics lang=az]