戻り値型のJavaメソッドはreturnステートメントなしでコンパイルされます


228

質問1:

次のコードがreturnステートメントなしでコンパイルされるのはなぜですか?

public int a() {
    while(true);
}

注意:しばらくしてからreturnを追加すると、が返されUnreachable Code Errorます。

質問2:

一方、次のコードがコンパイルされるのはなぜですか?

public int a() {
    while(0 == 0);
}

以下はそうではありませんが。

public int a(int b) {
    while(b == b);
}

2
2番目の質問の後半のおかげで、stackoverflow.com / questions / 16789832 / …の複製ではありません。
TJクラウダー2015

回答:


274

質問1:

次のコードがreturnステートメントなしでコンパイルされるのはなぜですか?

public int a() 
{
    while(true);
}

これはJLS§8.4.7でカバーされています

メソッドが戻り値の型を持つように宣言されている場合(§8.4.5)、メソッドの本体が正常に完了すると、コンパイル時エラーが発生します(§14.1)。

つまり、戻り値の型を持つメソッドは、値を返すreturnステートメントを使用することによってのみ戻る必要があります。このメソッドは、「本体の端をドロップオフする」ことはできません。メソッド本体のreturnステートメントに関する正確な規則については、§14.17を参照してください。

メソッドが戻り値の型を持ち、returnステートメントを含まない可能性があります。以下はその一例です。

class DizzyDean {
    int pitch() { throw new RuntimeException("90 mph?!"); }
}

コンパイラーはループが終了しないことを知っているため(trueもちろん、常にtrueです)、関数が「正常に戻る」ことができない(本体の最後をドロップする)ことができないことを知っているため、がないことは問題ありませんreturn

質問2:

一方、次のコードがコンパイルされるのはなぜですか?

public int a() 
{
    while(0 == 0);
}

以下はそうではありませんが。

public int a(int b)
{
    while(b == b);
}

この0 == 0場合、コンパイラーはループが終了しないことを認識しています(0 == 0常にtrueになります)。しかし、それについてはわかりませんb == b

何故なの?

コンパイラーは定数式(§15.28)を理解します。§15.2の 引用-式の形式(奇妙なことに、この文は§15.28にないため)

一部の式には、コンパイル時に決定できる値があります。これらは定数式です(§15.28)。

あなたのb == b例では、変数が含まれているため、定数式ではなく、コンパイル時に決定されるように指定されていません。我々は(場合ものの、常にこのような場合には真であることが起こっていることを確認できbdoubleQBruteとして、指摘し、我々は簡単にだまされてはいけ可能性がDouble.NaNある、ない==自体)が、JLS定数式はコンパイル時に決定されることのみを指定、それはコンパイラが非定数式を評価しようとすることを許可しません。bayou.io 理由を指摘しました。コンパイル時に変数を含む式を決定しようとする道を歩み始めたら、どこで停止しますか?b == b明らかである(非、NaN値)、しかしどうa + b == b + aですか?または(a + b) * 2 == a * 2 + b * 2?定数で線を引くことは理にかなっています。

したがって、式を「決定」しないので、コンパイラーはループが終了しないことを知らないため、メソッドが正常に戻ることができると考えていますreturn。これは、を使用する必要があるため、許可されていません。だからそれはの欠如について不平を言うreturn


34

メソッドの戻り値の型を、指定された型の値を返すという約束ではなく、指定された型ではない値を返さないという約束と考えると興味深いかもしれません。したがって、何も返却しない場合は、約束を破ることはないため、以下のいずれも合法です。

  1. 永遠にループする:

    X foo() {
        for (;;);
    }
  2. 永遠に再帰する:

    X foo() {
        return foo();
    }
  3. 例外をスローする:

    X foo() {
        throw new Error();
    }

(再帰について考えるのが楽しい1つだと思います:コンパイラーは、メソッドがタイプの値X(それが何であれ)を返すと信じていますが、それは真実ではありません。を入手するX。)


8

バイトコードを確認すると、返されたものが定義と一致しない場合は、コンパイルエラーが発生します。

例:

for(;;) バイトコードが表示されます:

L0
    LINENUMBER 6 L0
    FRAME SAME
    GOTO L0

戻りバイトコードがないことに注意してください

これは戻り値にヒットしないため、間違った型を返しません。

比較のために、次のような方法:

public String getBar() { 
    return bar; 
}

次のバイトコードを返します。

public java.lang.String getBar();
    Code:
      0:   aload_0
      1:   getfield        #2; //Field bar:Ljava/lang/String;
      4:   areturn

「参照を返す」ことを意味する「戻り」に注意してください

次のようにすると、

public String getBar() { 
    return 1; 
}

次のバイトコードを返します。

public String getBar();
  Code:
   0:   iconst_1
   1:   ireturn

これで、定義内の型がireturnの戻り型と一致しないことがわかります。つまり、return intです。

つまり、メソッドに戻りパスがある場合、そのパスは戻り値の型と一致する必要があります。ただし、バイトコードには、戻りパスがまったく生成されないため、ルールに違反しないインスタンスがあります。

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