javaコンパイラの奇妙さ:フィールドは同じクラスで宣言されているが、“見えない”
-
05-07-2019 - |
質問
eclipseコンパイラは、フィールドsが表示されないことを示す次のコードのコンパイルを拒否します。 (IBMのAspect Jコンパイラーも拒否し、「解決できなかった」と述べています)なぜですか?
public class Test {
String s;
void foo(Object o) {
String os = getClass().cast(o).s;
}
}
Java言語仕様の状態:
そうでなければ、デフォルトがあると言います アクセス。次の場合にのみ許可されます。 アクセスは内部から発生します 型が宣言されているパッケージ。
私が理解する方法では、フィールドは同じコンパイル単位で、したがって同じパッケージ内で宣言され、アクセスされるため、アクセスできるはずです。
さらに奇妙なことに、からダウンキャストを追加しますか? Test
を Test
に拡張すると、フィールドが表示されます。つまり、次のコードがコンパイルされます。
public class Test {
String s;
void foo(Object o) {
Test t = getClass().cast(o);
String os = t.s;
}
}
コンパイラのバグに遭遇したか、Java仕様を誤解しましたか?
編集: 今、別のコンピューターにいます。ここでは、javacはコードを受け入れますが、Eclipseはまだ受け入れません。このマシンのバージョン:
Eclipseプラットフォーム
バージョン:3.4.2ビルドID: M20090211-1700
JDK 1.6.0
編集2 実際、javacはコードを受け入れます。 IBMのAscpect Jコンパイラを使用するantビルドを実行してテストしました...
解決
これを試してください:
void foo(Object o) {
Test foo = getClass().cast(o);
String so = foo.s;
}
[明確にするために編集]:
getClass()。cast(o)
は、タイプ ' capture#1-of?
'を拡張します。そのため、この問題はジェネリックおよびコンパイラーによる処理方法に関連しています。ジェネリックの仕様の詳細はわかりませんが、一部のコンパイラ(ここのコメントごと)がコードを受け入れる場合、これは仕様のループホールであるか、これらのコンパイラの一部が仕様に完全に準拠していないことを示します。 / p>
Test
ではなく、Test
[最後の考え]:
ここでは、Eclipseコンパイラーが実際に(慎重に)正しいと考えています。オブジェクト o
は、実際にはTestの拡張(および別のパッケージで定義)である可能性があり、コンパイラは実際にそうであるかどうかを知る方法がありません。そのため、別のパッケージで定義されている拡張機能のインスタンスの最悪のケースとして処理しています。クラス Test
に final
修飾子を追加すると、フィールド s
へのアクセスが許可された場合、それは非常に正しいはずですが、そうではありません。
他のヒント
さて、見てみましょう。コンパイラは、パッケージ内のエンティティによって foo()
が呼び出されることを適切に保証できないため、 s
が表示されることを保証できないと思います。たとえば、追加
protected void bar() {
foo();
}
そしてサブクラス Banana
別のパッケージ
public void quux() { bar(); }
おっと! getClass()
は Banana
を生成しますが、これは s
を見ることができません。
編集:ある意味で、other.package.Bananaはフィールド s
を持っていません。バナナが同じパッケージに含まれていた場合、 独自の s
プロパティを保持でき、 Test
のを参照する必要があります。 s
super
経由。
あなたの言っていることを再現できません。これらはどちらも、警告、エラー、またはjavacを直接使用することなく正常にコンパイルされます。
WinXP、javac 1.6.0_16
いいえeclipse(v3.4.1、ビルドID:M20080911-1700)で試しましたが、最初の場合は次のように表示されます:
The field Test.s is not visible
少なくともコンパイラー準拠レベル1.6および1.5の場合。
面白いことに、クイック修正オプションを見ると、 Change to 's'
解像度がリストされています。もちろん、これは問題を解決しません。したがって、Eclipseコンパイラーとクイックフィックス「ジェネレーター」これについても異なる見解があるようです;-)
最初に取得したEclipseのコンパイラコンプライアンスレベル1.4(予想どおり)の場合
s cannot be resolved or is not a field
2番目の場合は
Type mismatch: cannot convert from Object to Test
コマンドラインで -source 1.4
と target -1.4
を直接指定すると、 javac
が最初のコードを示します
cannot find symbol
2番目の場合は
incompatible types
非常に奇妙です。 (私にとって)未知の理由で、eclipseコンパイラーは明示的なキャストを必要とします:
void foo(Object o) {
String os = ((Test)getClass().cast(o)).s;
}
コードはSunのJDKでキャストせずに完全にコンパイルされます(GNU / Linuxでバージョン1.6.0_16を実行しています)。