การประกาศและกำหนดค่าเริ่มต้นตัวแปรภายในสวิตช์ Java


100

ฉันมีคำถามบ้าๆเกี่ยวกับสวิตช์ Java

int key = 2;

switch (key) {
    case 1:
        int value = 1;
        break;
    case 2:
        value = 2;
        System.out.println(value);
        break;
    default:
        break;
}

กรณีที่ 1 - เมื่อkeyสองมันประสบความสำเร็จพิมพ์ค่าเป็น 2
กรณีที่ 2 - เมื่อฉันจะแสดงความคิดเห็นvalue = 2ในcase 2:มัน squawks บอกว่าค่าตัวแปรท้องถิ่นอาจจะไม่ได้รับการเริ่มต้น

คำถาม:

สถานการณ์ที่ 1: หากโฟลว์การดำเนินการไม่ไปที่case 1:(เมื่อ the key = 2) จะรู้ได้อย่างไรว่าประเภทของตัวแปรค่าเป็นintอย่างไร

สถานการณ์ที่ 2: หากคอมไพลเลอร์ทราบชนิดของตัวแปรค่าเป็นintดังนั้นคอมไพเลอร์จะต้องเข้าถึงint value = 1;นิพจน์ในcase 1:(การประกาศและการเริ่มต้น) แล้วทำไมไม่ได้ sqawrk เมื่อฉันกำลังจะไปแสดงความคิดเห็นvalue = 2ในการcase 2:พูดค่าตัวแปรท้องถิ่นอาจจะไม่ได้รับการเริ่มต้น


13
ไม่ใช่คำถามบ้าบอ แต่เป็นคำถามที่ดีมาก
biziclop


@PhilippeCarriere จริงๆแล้วฉันคิดว่ามันควรจะกลับกัน - คำตอบที่นี่ดีกว่า (แม้ว่าโพสต์จะใหม่กว่าก็ตาม) เนื่องจากมีการอ้างอิงโดยตรงไปยัง JLS และสรุปปัญหาที่ครอบคลุมด้วยคำตอบที่แตกต่างกันในโพสต์นั้น ดูเพิ่มเติม
Tunaki

@Tunaki คำอธิบายสำหรับรายการที่ซ้ำกันเริ่มต้นด้วย "คำถามนี้ถูกถามมาก่อน" ฉันกำลังอ่านว่าอันหลังควรถูกทำเครื่องหมายว่าซ้ำกับก่อนหน้านี้ แต่ฉันยอมรับว่าอันนี้มีองค์ประกอบที่ดี บางทีพวกเขาควรจะรวมเข้าด้วยกัน?
Philippe Carriere

นอกจากนี้คำถามมากมายเกี่ยวกับ SO ยังถูกทำเครื่องหมายว่าซ้ำกับคำถามเดิมของฉันดังนั้นหากคุณตัดสินใจว่าควรทำเครื่องหมายคำถามนี้เป็นคำถามเดิมใหม่โปรดแก้ไขลิงก์ทั้งหมดเพื่ออ้างถึงคำถามนี้แทนของฉัน
Philippe Carriere

คำตอบ:


115

คำสั่ง Switch นั้นแปลกในแง่ของการกำหนดขอบเขตโดยทั่วไป จากส่วน 6.3 ของ JLS :

ขอบเขตของการประกาศตัวแปรโลคัลในบล็อก (§14.4) คือส่วนที่เหลือของบล็อกที่การประกาศปรากฏขึ้นโดยเริ่มจากตัวเริ่มต้นของตัวเองและรวมถึงตัวประกาศอื่น ๆ ทางด้านขวาในคำสั่งการประกาศตัวแปรโลคัล

ในกรณีของคุณcase 2อยู่ในบล็อกเดียวกับcase 1และปรากฏตามหลังแม้ว่าcase 1จะไม่ดำเนินการ ... ดังนั้นตัวแปรโลคัลจึงอยู่ในขอบเขตและพร้อมสำหรับการเขียนแม้ว่าคุณจะไม่ "ดำเนินการ" การประกาศตามหลักเหตุผล (การประกาศไม่ใช่ "ปฏิบัติการ" จริงๆแม้ว่าการเริ่มต้นจะเป็น)

หากคุณแสดงความคิดเห็นในการvalue = 2;มอบหมายคอมไพเลอร์ยังคงรู้ว่าคุณกำลังอ้างถึงตัวแปรใด แต่คุณจะไม่ผ่านเส้นทางการดำเนินการใด ๆ ที่กำหนดค่าให้นั่นคือสาเหตุที่คุณได้รับข้อผิดพลาดเหมือนที่คุณต้องการเมื่อคุณพยายาม อ่านตัวแปรโลคัลอื่น ๆ ที่ไม่ได้กำหนดแน่นอน

ฉันขอแนะนำอย่างยิ่งให้คุณไม่ใช้ตัวแปรท้องถิ่นที่ประกาศไว้ในกรณีอื่น ๆ ซึ่งจะทำให้โค้ดสับสนอย่างที่คุณเคยเห็น เมื่อฉันแนะนำตัวแปรท้องถิ่นในคำสั่งสวิตช์ (ซึ่งฉันพยายามทำน้อยครั้ง - กรณีควรสั้นมากตามหลักการแล้ว) ฉันมักจะแนะนำขอบเขตใหม่:

case 1: {
    int value = 1;
    ...
    break;
}
case 2: {
    int value = 2;
    ...
    break;
}

ฉันเชื่อว่าสิ่งนี้ชัดเจนกว่า


11
+1 สำหรับ "การประกาศไม่ได้เป็น" ปฏิบัติการ "จริงๆแม้ว่าการเริ่มต้นจะเป็น" และขอบคุณสำหรับคำแนะนำ Skeet ด้วย
namalfernandolk

1
ด้วยการรวม JEP-325 รูปภาพของขอบเขตของตัวแปรโลคัลได้เปลี่ยนไปและสามารถใช้ชื่อเดียวกันข้ามเคสแทนการสลับบล็อกได้ แม้ว่าจะอาศัยการเข้ารหัสบล็อกที่คล้ายกันเช่นกัน นอกจากนี้ค่าที่กำหนดให้กับตัวแปรต่อกรณีสวิตช์จะสะดวกมากกับนิพจน์สวิตช์
Naman

คะแนนสำหรับการเพิ่มขอบเขตใหม่ด้วยวงเล็บปีกกา ไม่รู้ด้วยซ้ำว่าคุณทำได้
Sunfish ลอยน้ำ

21

มีการประกาศตัวแปร (เป็น int) แต่ไม่ได้กำหนดค่าเริ่มต้น (กำหนดค่าเริ่มต้น) นึกถึงบรรทัด:

int value = 1;

เช่น:

int value;
value = 1;

int valueส่วนหนึ่งบอกคอมไพเลอร์ที่รวบรวมเวลาที่คุณมีตัวแปรค่าที่เรียกว่าซึ่งเป็น int value = 1ส่วนหนึ่งเริ่มต้นมัน แต่ที่เกิดขึ้นที่ใช้เวลาและจะไม่เกิดขึ้นเลยถ้าสาขาของสวิทช์ที่ไม่ได้เข้ามา


+1 สำหรับคำอธิบายที่ดีเกี่ยวกับการประกาศและการเริ่มต้นในเวลาคอมไพล์และรันไทม์
namalfernandolk

18

จาก http://www.coderanch.com/t/447381/java-programmer-SCJP/certification/variable-initialization-within-case-block

การประกาศจะถูกประมวลผลในเวลาคอมไพล์และไม่ขึ้นอยู่กับขั้นตอนการดำเนินการของโค้ดของคุณ เนื่องจากvalueมีการประกาศภายในขอบเขตเฉพาะของบล็อกสวิตช์จึงสามารถใช้งานได้ทุกที่ในบล็อกนั้นจากจุดที่ประกาศ


1
ทำไมคำตอบนี้ถึงได้รับการโหวต มันไม่ตอบคำถามไม่เหมือนกับคำตอบของ paul หรือ skeet ...
Dhruv Gairola

7
มัน. ดังนั้น +1 เพนนีจากด้านข้างของฉันด้วย
Ravinder Reddy

3

ด้วยการรวมJEP 325: Switch Expressions (Preview)ใน JDK-12 Early Access builds มีการเปลี่ยนแปลงบางอย่างที่สามารถเห็นได้จากคำตอบของจอน -

  1. ขอบเขตตัวแปรท้องถิ่น - ตัวแปรท้องถิ่นในกรณีที่สวิทช์ขณะนี้สามารถท้องถิ่นกับกรณีของตัวเองแทนการบล็อกสวิทช์ทั้งหมด ตัวอย่าง (คล้ายกับสิ่งที่จอนพยายามใช้วากยสัมพันธ์เช่นกัน) พิจารณาDayคลาส enum เพื่ออธิบายเพิ่มเติม:

    public enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    
    // some another method implementation
    Day day = Day.valueOf(scanner.next());
    switch (day) {
        case MONDAY,TUESDAY -> {
            var temp = "mon-tue";
            System.out.println(temp);
        }
        case WEDNESDAY,THURSDAY -> {
            var temp = Date.from(Instant.now()); // same variable name 'temp'
            System.out.println(temp);
        }
        default ->{
            var temp = 0.04; // different types as well (not mandatory ofcourse)
            System.out.println(temp);
        }
    }
    
  2. เปลี่ยนนิพจน์ - หากเจตนาคือการกำหนดค่าให้กับตัวแปรแล้วใช้ประโยชน์จากมันหนึ่งครั้งสามารถใช้ประโยชน์จากนิพจน์สวิตช์ได้ เช่น

    private static void useSwitchExpression() {
        int key = 2;
        int value = switch (key) {
            case 1 ->  1;
            case 2 -> 2;
            default -> {break 0;}
        };
        System.out.println("value = " + value); // prints 'value = 2'
    }
    

0

คำอธิบายนี้อาจช่วยได้

    int id=1;

    switch(id){
        default: 
            boolean b= false; // all switch scope going down, because there is no scope tag

        case 1:
            b = false;
        case 2:{
            //String b= "test"; you can't declare scope here. because it's in the scope @top
            b=true; // b is still accessible
        }
        case 3:{
            boolean c= true; // case c scope only
            b=true; // case 3 scope is whole switch
        }
        case 4:{
            boolean c= false; // case 4 scope only
        }
    }

0

ข้อมูลจำเพาะของ Java:

https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.11

กรณีของการเสร็จสิ้นอย่างกะทันหันเนื่องจากการหยุดพักด้วยป้ายกำกับจะถูกจัดการโดยกฎทั่วไปสำหรับข้อความที่มีป้ายกำกับ (§14.7)

https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.7

ข้อความที่มีป้ายกำกับ:

LabeledStatement: Identifier: Statement

LabeledStatementNoShortIf: Identifier: StatementNoShortIf

ซึ่งแตกต่างจาก C และ C ++ ภาษาโปรแกรม Java ไม่มีคำสั่ง goto ป้ายข้อความตัวระบุใช้กับคำสั่ง break (§14.15) หรือต่อ (§14.16) ที่ปรากฏที่ใดก็ได้ภายในคำสั่งที่มีป้ายกำกับ

ขอบเขตของฉลากของคำสั่งที่มีป้ายกำกับคือคำชี้แจงที่มีอยู่ในทันที

กล่าวอีกนัยหนึ่งกรณีที่ 1 กรณีที่ 2 เป็นป้ายกำกับภายในคำสั่งสวิตช์ สามารถใช้คำสั่ง break และ Continue กับป้ายกำกับได้

เนื่องจากเลเบลใช้ขอบเขตของคำสั่งร่วมกันตัวแปรทั้งหมดที่กำหนดภายในเลเบลจึงแบ่งขอบเขตของคำสั่งสวิตช์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.