בשביל מה switch קיים
switch משווה ערך אחד מול רשימה של אפשרויות קבועות ומריץ את הענף שמתאים. כשאתם מוצאים את עצמכם כותבים שרשרת ארוכה של if/else if שבודקת שוב ושוב את אותו משתנה מול קבועים שונים, switch אומר את אותו הדבר בצורה ברורה יותר.
הוא עושה רק בדיקות שוויון מול קבועים שידועים בזמן קומפילציה: הוא לא יכול לבדוק טווחים או לשלב תנאים. לדברים כאלה הישארו עם if/else.
משפט switch הקלאסי
הצורה המסורתית משתמשת בתוויות case עם נקודתיים וב-break אחרי כל ענף:
Java מחשבת את day, קופצת ל-case המתאים, מריצה את הפקודות שלו, וה-break יוצא מה-switch. ענף ה-default רץ כששום דבר אחר לא מתאים: חשבו עליו כעל ה-else. הוא אופציונלי, אבל מומלץ לכלול אותו כדי שערכים לא צפויים לא יחמקו בשקט.
מלכודת ה-break: נפילה הלאה (fall-through)
זה הבאג הנפוץ ביותר ב-switch. אם משמיטים break, הביצוע לא עוצר בסוף ה-case המתאים: הוא נופל אל ה-case הבא וממשיך לרוץ עד שהוא פוגש break או את הסוגר המסולסל הסוגר:
אולי ציפיתם לקבל רק Level 1, אבל התוכנית מדפיסה את שלוש השורות. מכיוון של-case 1 אין break, השליטה מחליקה ישר אל case 2 ו-case 3. הוסיפו תמיד break, אלא אם אתם באמת רוצים נפילה הלאה, וכשאתם כן מסתמכים עליה, השאירו הערה כדי שהקורא הבא יידע שזה בכוונה.
קיבוץ מקרים
לנפילה הלאה יש שימוש לגיטימי ומסודר אחד: לערום תוויות case כך שכמה ערכים יחלקו בלוק אחד. שימו את התוויות אחת אחרי השנייה בלי קוד ביניהן:
כאן 'A', 'B' ו-'C' מריצים כולם את אותה שורת "Pass". זו הדרך המיועדת לומר "כל אחד מהערכים האלה עושה את אותו הדבר".
switch על מחרוזות
אתם לא מוגבלים למספרים. מאז Java 7 אפשר לעשות switch על String, וזה מושלם לבחירות מתפריט או לשמות של פקודות:
ההתאמה רגישה לאותיות גדולות וקטנות: "Stop" לא יתאים ל-"stop", ולכן כדאי לנרמל קודם (למשל command.toLowerCase()) אם הקלט מגיע באותיות שונות. עוד מלכודת אחת: אם command הוא null, ה-switch זורק NullPointerException, אז הגנו מפני null לפני שמגיעים אליו.
תחביר החץ המודרני
גרסאות Java חדשות יותר (14 ומעלה) מוסיפות צורת חץ, case label -> ..., שמתקנת את מלכודת הנפילה כבר בתכנון. כל case עם חץ מריץ בדיוק ענף אחד ואף פעם לא נופל הלאה, כך שאין break שאפשר לשכוח:
שימו לב שמקבצים ערכים עם פסיק (case 6, 7) במקום לערום תוויות, ואין אף break באופק. לכמה פקודות משתמשים בבלוק: case 1 -> { ...; ... }. העדיפו את הצורה הזו בקוד חדש: היא קצרה יותר ומעלימה סוג שלם של באגים.
switch כביטוי
צורת החץ יכולה גם להפיק ערך. ביטוי switch מחזיר תוצאה שאפשר להשים ישירות למשתנה, בלי משתנה זמני ובלי השמה חוזרת בכל ענף:
כל ה-switch (...) { ... } מחושב למספר שנשמר ב-days. שימו לב לנקודה-פסיק אחרי הסוגר המסולסל: היא חלק מפקודת ההשמה. ביטוי switch חייב להיות ממצה (לכסות כל ערך אפשרי), ולכן יש כאן default. שימו לב גם שענף שלא מתאים לשום ערך יכול לזרוק חריגה במקום להחזיר ערך. אם ענף צריך כמה פקודות לפני שהוא מחזיר ערך, השתמשו בבלוק עם yield:
case 2 -> {
boolean leap = (year % 4 == 0);
yield leap ? 29 : 28;
}
yield הוא הדרך שבה בלוק חץ מחזיר את הערך שלו, המקבילה בעולם הביטויים ל-break שנושא תוצאה.
הבא בתור: לולאות for
switch בוחר ענף אחד מתוך רבים; לפעמים צריך דווקא לחזור על בלוק הרבה פעמים. לולאת for מריצה קוד מספר מבוקר של פעמים, סופרת לאורך טווח או עוברת על כל פריט, והיא הנושא של העמוד הבא.
שאלות נפוצות
מתי כדאי להשתמש ב-switch במקום if-else ב-Java?
השתמשו ב-switch כשמשווים ערך אחד מול הרבה אפשרויות קבועות, כמו מספר של יום, בחירה מתפריט או enum. הוא קריא יותר משרשרת ארוכה של if/else if ומבהיר ש"בוחרים ענף אחד מתוך ערכים ידועים". הישארו עם if/else כשהתנאים כוללים טווחים (x > 10), כמה משתנים או כל דבר שאינו בדיקת שוויון פשוטה מול קבוע.
למה צריך break במשפט switch ב-Java?
בתחביר הקלאסי עם נקודתיים, ברגע ש-case מתאים, הביצוע נופל הלאה אל המקרים הבאים עד שהוא פוגש break או את סוף ה-switch. break עוצר את זה. לשכוח אותו זה באג קלאסי: מתאימים ל-case 1: ובטעות מריצים גם את הקוד של case 2:, case 3: וכן הלאה. תחביר החץ החדש יותר (case 1 -> ...) אף פעם לא נופל הלאה, ולכן לא כותבים break בכלל.
האם switch ב-Java עובד עם מחרוזות?
כן. מאז Java 7 אפשר לעשות switch על String, וגם על int, char, byte, short, טיפוסי העטיפה שלהם וערכי enum. אי אפשר לעשות switch על long, double, float או boolean. ההתאמה של מחרוזות רגישה לאותיות גדולות וקטנות, כך ש-"Yes" ו-"yes" הם מקרים שונים.