Javaスイッチ内での変数の宣言と初期化


99

Javaスイッチについて、おかしな質問があります。

int key = 2;

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

シナリオ1 - key2として、それが正常値を出力2である
私がコメントするつもりだ-シナリオ2 value = 2case 2:、それは言ってsquawks ローカル変数の値が初期化されていないかもしれません

質問:

シナリオ1:実行フローがに進まないcase 1:場合(の場合key = 2)、値変数の型はどのようにしてわかりintますか?

シナリオ2:コンパイラーが値変数のタイプをとして認識している場合は、。(宣言と初期化)の式にintアクセスしている必要があります。では、なぜローカル変数の値が初期化されていない可能性があると言ってコメントするつもりなのでしょうか。int value = 1;case 1:value = 2case 2:


13
クレイジーな質問ではなく、とても良い質問です。
biziclop


@PhilippeCarriere実際には、それは逆になっていると思います-JLSへの直接参照があるため、ここの答えは(投稿が新しい場合でも)より良く、その投稿のさまざまな答えでカバーされている問題をうまくまとめています。もご覧ください
Tunaki 2016年

@Tunaki重複の説明は、「この質問は以前に尋ねられました」で始まります。後の方は前の方の複製としてマークされるべきなので、私はそれを読んでいます。しかし、これには素晴らしい要素があることに私は同意します。多分それらはどうにかして統合されるべきですか?
フィリップカリエール2016年

また、SOに関する多くの質問が私の元の質問の重複としてマークされているため、これを新しい元の質問としてマークする方がよいと判断した場合は、私のリンクではなく、このリンクを参照するようにすべてのリンクを修正してください。
フィリップカリエール2016年

回答:


114

基本的に、switchステートメントのスコープは奇妙です。JLSのセクション6.3から:

ブロック内のローカル変数宣言のスコープ(§14.4)は、宣言が出現するブロックの残りの部分であり、独自のイニシャライザから始まり、ローカル変数宣言ステートメントの右側にさらに宣言子を含めます。

あなたの場合、case 2は実行されませんが同じブロックcase 1あり、その後に表示されcase 1ます...したがって、宣言を論理的に「実行」しないにもかかわらず、ローカル変数はスコープ内にあり、書き込み可能です。(宣言は、初期化はできますが、実際には「実行可能」ではありません。)

value = 2;割り当てをコメントアウトしても、コンパイラは参照している変数を認識していますが、値を割り当てる実行パスを通過していません。そのため、次のようなエラーが発生します。その他の明確に割り当てられていないローカル変数を読み取ります。

他の場合に宣言されたローカル変数を使用しないことを強くお勧めします。これまで見てきたように、コードが非常に混乱します。switchステートメントにローカル変数を導入するとき(めったにしないようにします-ケースは非常に短くするのが理想的ですが)、通常は新しいスコープを導入することを好みます。

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

これはもっと明確だと思います。


11
「初期化はできるが、宣言は実際には「実行可能」ではない。」の+1。アドバイスもありがとうスキート。
namalfernandolk 2012年

1
JEP-325が統合されたため、ローカル変数のスコープの図が変更され、スイッチブロックの代わりに、ケース全体で同じ名前を使用できます。同様のブロックコーディングにも依存していますが。また、スイッチケースごとに変数に割り当てられる値は、スイッチ式で非常に便利です。
ナマン2018

ブレース付きの新しいスコープを追加するためのポイント。あなたがそれができるとさえ知らなかった。
フローティングサンフィッシュ2018

21

変数は(intとして)宣言されていますが、初期化されていません(初期値が割り当てられています)。次の行について考えてみます。

int value = 1;

なので:

int value;
value = 1;

このint value部分は、intであるvalueという変数があることをコンパイル時にコンパイラに通知します。value = 1一部はそれを初期化しますが、それは実行時に発生し、スイッチの枝が入力されていない場合は、すべてでは発生しません。


+1は、コンパイル時と実行時の宣言と初期化のわかりやすい説明です。
namalfernandolk 2012年

18

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

宣言はコンパイル時に処理され、コードの実行フローには依存しません。valueはswitchブロックのローカルスコープ内で宣言されているため、宣言の時点からそのブロック内のどこでも使用できます。


1
なぜこの回答は賛成されているのですか?ポールやスキートの答えとは異なり、質問には答えません...
Dhruv Gairola '30

7
します。だから、私の側からも、+ 1ペニー。
Ravinder Reddy、

3

JEP 325の統合 JDK-12のスイッチ式(プレビュー)の早期アクセスビルド。ジョンの答えから見ることができる特定の変更があります -

  1. ローカル変数スコープ -スイッチケースのローカル変数を、スイッチブロック全体ではなく、ケース自体ローカルにできるようになりました。Day詳細な説明のために列挙型クラスを検討する例(ジョンが構文的にも試みたものと同様):

    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:識別子:StatementNoShortIf

CおよびC ++とは異なり、Javaプログラミング言語にはgotoステートメントがありません。識別子ステートメントラベルは、ラベル付きステートメント内のどこかに現れるbreak(§14.15)またはcontinue(§14.16)ステートメントで使用されます。

ラベル付きステートメントのラベルのスコープは、直接含まれるステートメントです。

つまり、ケース1、ケース2は、switchステートメント内のラベルです。breakおよびcontinueステートメントはラベルに適用できます。

ラベルはステートメントのスコープを共有するため、ラベル内で定義されたすべての変数は、switchステートメントのスコープを共有します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.