6

興味深いコードに出くわしたとき、私が支援している代替のオープンソース JVM ( Avian )のあいまいなテストケースを考え出そうとしていましたが、コンパイルされなかったことに驚きました:

public class Test {
    public static int test1() {
        int a;
        try {
            a = 1;
            return a; // this is fine
        } finally {
            return a; // uninitialized value error here
        }
    }
    public static void main(String[] args) {
        int a = test1();
    }
}

最も明白なコード パス (私が目にする唯一のもの) は、a = 1 を実行して a を返すように "試行" し (初めて)、最後に実際に a を返すを実行することです。ただし、 javac は、「a」が初期化されていない可能性があると不平を言っています。

    Test.java:8: 変数 a が初期化されていない可能性があります  
        を返します。  
               ^  

私が考えることができる唯一のことは、別のコード パスを引き起こす/許可する可能性があるということです。しかし、これらがコードのこの場所で発生する可能性があるケースは考えられません。

Java 標準の詳細に精通している人は、これに光を当てることができますか? これは、コンパイラが保守的であるため、そうでなければ有効なコードであるものをコンパイルすることを拒否しているという単なるケースですか?それとも、ここで何か奇妙なことが起こっているのでしょうか?

4

7 に答える 7

10

a=1 行で例外が発生する可能性があるのに、JVM エラーが発生する可能性があるというのは直感に反するように思えるかもしれません。したがって、変数は初期化されていません。したがって、コンパイラ エラーは完全に理にかなっています。これは、あなたが言及したあいまいなランタイム エラーです。ただし、OutOfMemoryError はあいまいではなく、少なくとも開発者は考慮する必要があると私は主張します。さらに、OutOfMemoryError を設定する状態は別のスレッドで発生する可能性があり、制限を超えて使用されるヒープ メモリの量をプッシュする 1 つのアクションは、変数 a の割り当てであることを思い出してください。

とにかく、あなたはコンパイラの設計を見ているので、finally ブロックで値を返すことがいかにばかげているかをすでに知っていると思います。

于 2009-08-08T06:54:43.827 に答える
7

Java 言語仕様では、変数を使用する前に変数を割り当てる必要があります。はJLS、「明確な割り当て」ルールと呼ばれる特定のルールを定義します。すべての Java コンパイラは、これらに準拠する必要があります。

JLS 16.2.15:

V が try ステートメントの前に確実に割り当てられている場合、V は finally ブロックの前に確実に割り当てられています。

try-catch-finallyつまり、finally ステートメントを検討する場合、ステートメント割り当て内の try および catch ブロック ステートメントは考慮されません。

言うまでもなく、その仕様はここでは非常に保守的ですが、寛大であるが理解や推論が困難であるよりも、仕様が少し制限されている (ルールはすでに複雑であると信じている) シンプルであることを望んでいます。

コンパイラはこれらの明確な割り当て規則に従う必要があるため、すべてのコンパイラが同じエラーを発行します。JLSコンパイラは、エラーを抑制するために指定された以上の分析を実行することは許可されていません。

于 2009-08-08T06:58:15.923 に答える
3

それは、try-catch-finally 関係のセマンティクスによるものだと思います。Java言語仕様から:

try ブロックの実行が正常に完了すると、finally ブロックが実行されます...

値 V のスローにより、try ブロックの実行が突然終了した場合...

try ブロックの実行が R 以外の理由で突然終了した場合、finally ブロックが実行されます...

ここでは、最後のケースが最も関連性が高いようです。try ブロックがなんらかの理由で突然完了した場合、finally ブロックは正しく実行できるはずです。明らかに、割り当ての前に try ブロックが終了した場合、finally ブロックは無効になります。ただし、あなたが言ったように、これは特に可能性が高いわけではありません。

于 2009-08-08T06:19:05.817 に答える
2

コンパイラーが次の (後続の) ステートメントが

int i=5;int d;
if(i<10)
{system.out.println(d);}

のように条件文が確実で間違いのないコードにたどり着かない場合はコンパイラエラーは発生しません。

int i;

if(true){}

else
{System.out.println(d);}

条件文が確実に発生し、疑いのないコードに到達すると、コンパイラ エラーが発生します。

int i;
if(true)
{System.out.println(d);}
else{}

try ブロックはこの下にあるため、同じルールに従います。

于 2012-11-01T17:09:57.303 に答える
2

代入中であっても、try ブロック内の任意の時点で例外が発生する可能性があるため、finally が初期化されていない変数を返す可能性があるという包括的な仮定を行うために、javac が必要になる可能性が非常に高くなります。理論的には、詳細な分析を行い、try ブロック 'a' を通るすべてのパスで常に正常に初期化されることを発見できますが、それは多くの作業でほとんど利益はありません。

誰かがJava言語仕様の関連セクションを指摘できれば...

于 2009-08-08T06:19:20.013 に答える
1

Javaコンパイラは最悪のケースを想定していると思います.tryブロック内の何かが何らかの理由で実行されるという保証はありません。したがって、その苦情は有効です。変数が初期化されていない可能性があります。

于 2009-08-08T07:24:49.957 に答える
0

コンパイラはここで保守的です。

于 2009-08-08T06:11:18.420 に答える