Mendeklarasikan dan menginisialisasi variabel dalam switch Java

99

Saya punya pertanyaan gila tentang sakelar Java.

int key = 2;

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

Skenario 1 - Ketika keykeduanya berhasil mencetak nilai sebagai 2.
Skenario 2 - Ketika saya akan berkomentar value = 2di case 2:dalamnya bertengkar mengatakan nilai variabel lokal mungkin belum diinisialisasi .

Pertanyaan:

Skenario 1: Jika aliran eksekusi tidak berjalan ke case 1:(ketika key = 2), lalu bagaimana cara mengetahui jenis variabel nilai int?

Skenario 2: Jika kompilator mengetahui tipe variabel nilai sebagai int, maka ia harus mengakses int value = 1;ekspresi in case 1:. (Deklarasi dan Inisialisasi). Lalu mengapa hal itu sqawrk Ketika aku akan berkomentar value = 2di case 2:, mengatakan nilai variabel lokal mungkin tidak telah diinisialisasi .

namalfernandolk.dll
sumber
13
Ini bukan pertanyaan gila, ini pertanyaan yang sangat bagus.
biziclop
Kemungkinan duplikat lingkup Variabel dalam kasus sakelar
Philippe Carriere
@PhilippeCarriere Sebenarnya, menurut saya seharusnya terbalik - jawabannya di sini lebih baik (meskipun posnya lebih baru) karena ada referensi langsung ke JLS, dan merangkum dengan baik masalah yang tercakup dalam jawaban berbeda di pos itu. Lihat juga .
Tunaki
@Tunaki Deskripsi untuk duplikat dimulai dengan "Pertanyaan ini telah ditanyakan sebelumnya". Saya membaca bahwa yang nanti harus ditandai sebagai duplikat dari yang sebelumnya. Tetapi saya setuju bahwa yang ini memiliki elemen yang bagus. Mungkinkah mereka harus digabungkan?
Philippe Carriere
Juga banyak pertanyaan tentang SO ditandai sebagai duplikat dari pertanyaan asli saya, jadi jika Anda memutuskan bahwa lebih baik menandai yang ini sebagai yang baru, harap perbaiki semua tautan untuk merujuk ke pertanyaan ini, bukan milik saya.
Philippe Carriere

Jawaban:

114

Pernyataan switch ganjil dalam hal pelingkupan, pada dasarnya. Dari bagian 6.3 JLS :

Cakupan deklarasi variabel lokal dalam sebuah blok (§14.4) adalah blok lainnya di mana deklarasi tersebut muncul, dimulai dengan penginisialisasinya sendiri dan termasuk setiap deklarator di sebelah kanan dalam pernyataan deklarasi variabel lokal.

Dalam kasus Anda, case 2berada di blok yang sama dengan case 1dan muncul setelahnya, meskipun case 1tidak akan pernah dieksekusi ... jadi variabel lokal ada dalam cakupan dan tersedia untuk ditulis meskipun Anda secara logis tidak pernah "menjalankan" deklarasi. (Deklarasi tidak benar-benar "dapat dieksekusi" meskipun inisialisasi adalah.)

Jika Anda mengomentari value = 2;tugas, kompilator masih tahu variabel mana yang Anda rujuk, tetapi Anda tidak akan melalui jalur eksekusi apa pun yang memberinya nilai, itulah sebabnya Anda mendapatkan kesalahan seperti yang Anda lakukan ketika mencoba membaca variabel lokal lain yang tidak ditetapkan secara pasti.

Saya sangat menyarankan Anda untuk tidak menggunakan variabel lokal yang dideklarasikan dalam kasus lain - ini mengarah ke kode yang sangat membingungkan, seperti yang Anda lihat. Ketika saya memperkenalkan variabel lokal dalam pernyataan switch (yang jarang saya coba lakukan - case harus sangat pendek, idealnya) saya biasanya lebih suka memperkenalkan ruang lingkup baru:

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

Saya yakin ini lebih jelas.

Jon Skeet
sumber
11
1 untuk "Deklarasi tidak benar-benar" dapat dijalankan "meskipun inisialisasi ada.". Dan terima kasih atas nasehatnya juga Skeet.
namalfernandolk
1
Dengan JEP-325 terintegrasi, gambaran lingkup variabel lokal telah berubah dan seseorang dapat menggunakan nama yang sama di seluruh kasus daripada blok saklar. Meskipun itu bergantung pada pengkodean blok yang serupa juga. Selain itu, nilai yang ditetapkan ke variabel per kasus sakelar akan jauh lebih nyaman dengan ekspresi sakelar.
Naman
Poin untuk menambahkan ruang lingkup baru dengan kawat gigi. Bahkan tidak tahu kamu bisa melakukan itu.
Floating Sunfish
21

Variabel telah dideklarasikan (sebagai int), tetapi tidak diinisialisasi (diberi nilai awal). Pikirkan barisnya:

int value = 1;

Sebagai:

int value;
value = 1;

Bagian tersebut int valuememberi tahu kompilator pada waktu kompilasi bahwa Anda memiliki variabel bernama nilai yang merupakan int. Bagian ini value = 1menginisialisasinya, tetapi itu terjadi pada waktu proses, dan tidak terjadi sama sekali jika cabang sakelar itu tidak dimasukkan.

Paul
sumber
1 untuk penjelasan yang bagus tentang deklarasi dan inisialisasi dalam waktu kompilasi dan runtime.
namalfernandolk
18

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

Deklarasi diproses pada waktu kompilasi dan tidak bergantung pada alur eksekusi kode Anda. Karena valuedideklarasikan dalam lingkup lokal dari blok switch, ia dapat digunakan dimanapun di blok itu dari titik deklarasinya.

Sampah
sumber
1
mengapa jawaban ini diberi suara positif? itu tidak menjawab pertanyaan, tidak seperti jawaban paul atau skeet ...
Dhruv Gairola
7
Memang. Jadi, +1, satu sen, dari sisi saya juga.
Ravinder Reddy
3

Dengan integrasi JEP 325: Switch Expressions (Preview) di JDK-12 akses awal build. Ada beberapa perubahan yang bisa dilihat dari jawaban Jon -

  1. Lingkup Variabel Lokal - Variabel lokal dalam kasus sakelar sekarang dapat menjadi lokal untuk kasing itu sendiri alih-alih seluruh blok sakelar . Contoh (mirip dengan apa yang Jon telah coba secara sintaksis juga) mempertimbangkanDaykelas enum untuk penjelasan lebih lanjut:

    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. Tukar Ekspresi - Jika tujuannya adalah untuk menetapkan nilai ke variabel dan kemudian menggunakannya, sekali saja dapat menggunakan ekspresi sakelar. misalnya

    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'
    }
Naman
sumber
0

Penjelasan ini mungkin bisa membantu.

    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
        }
    }
Java jansen
sumber
0

Spesifikasi Java:

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

Kasus penyelesaian mendadak karena pemutusan dengan label ditangani oleh aturan umum untuk pernyataan berlabel (§14.7).

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

Pernyataan berlabel:

LabeledStatement: Identifier: Statement

LabeledStatementNoShortIf: Identifier: StatementNoShortIf

Tidak seperti C dan C ++, bahasa pemrograman Java tidak memiliki pernyataan goto; label pernyataan pengenal digunakan dengan pernyataan putus (§14.15) atau lanjutkan (§14.16) yang muncul di mana saja dalam pernyataan berlabel.

Ruang lingkup label dari pernyataan berlabel adalah Pernyataan yang langsung terkandung.

Dengan kata lain, kasus 1, kasus 2 adalah label di dalam pernyataan sakelar. pernyataan istirahat dan lanjutkan dapat diterapkan ke label.

Karena label berbagi ruang lingkup pernyataan, semua variabel yang ditentukan dalam label berbagi ruang lingkup pernyataan sakelar.

Pavel Molchanov
sumber