私が書いたJavaコード私はビルダーパターンが継承することのきれいな方法を達成するためにジェネリック医薬品の再帰的な形式を使用します。
この作品は、私は、私はJavaコンパイラから取得警告とエラーのいくつかを理解していません。
これは私が理解していない一部の深刻な簡略化されたバージョンです。
package nl.basjes.test;
public class Foo<X extends Foo<X>> {
public X doSomething() {
return this;
}
}
「これを返します。」私はエラーを取得します
Incompatible Types
Required: X
Found : nl.basjes.test.Foo <X>
今、「これは」と定義されてはFoo(あるいはフーそのもの)と「X」のサブクラスは常にありますX extends Foo<X>
。私の知る限りではこれらは「同じ」でなければなりませんが、どうやらそうではありません。
だから私のコードで私はこのようなreturn文にキャストを追加しました:
package nl.basjes.test;
public class Foo<X extends Foo<X>> {
public X doSomething() {
return (X)this;
}
}
その期待と意図したように、コードのコンパイルと仕事になります。
しかし私は、今でも上記と同様の理由で、「未検査キャスト」についての警告を得るのです(しかし、今、それは単なる警告です)。
$ javac -Xlint:unchecked nl/basjes/test/Foo.java
nl/basjes/test/Foo.java:5: warning: [unchecked] unchecked cast
return (X)this;
^
required: X
found: Foo<X>
where X is a type-variable:
X extends Foo<X> declared in class Foo
1 warning
なぜJavaはそれを見ていないX
(延びているFoo<X>
)とthis
(延びているFoo<X>
)互換性がありますか?
この時点で私の最高の推測では、これは私がまだ理解していないことを型消去の一部としなければならないということです。
あなたは具体的な型の引数を考えると、それは問題を見ることが容易になります。
思います
Foo<Bar> barFoo = ...;
あなたが呼び出すとbarFoo.doSomething()
、あなたが得ることを期待するBar
オブジェクトを:
Bar bar = barFoo.doSomething()
しかし、あなたの実際の実装:
public X doSomething() {
return this;
}
おおよそ以下の具体的なパラメータを充填することができます。
public Bar doSomething() {
return this; //But "this" is a Foo<Bar>, not a Bar.
}
ここではそれがさらに明白にする別の例です:
class Bar extends Foo<Bar> {
}
class Baz extends Foo<Bar> { //note this is a Foo<Bar>
}
そして:
Baz baz = new Baz();
Bar bar = baz.doSomething();
上記では、あなたが期待してbaz.doSomething()
返すことBar
が、内のコードをdoSomething()
返しているBaz
が、それをキャストするBar
(実際には、これらの型は互換性がありませんが、あなたに異なるクラスを持っているときにのみ、ClassCastExceptionが取得し、型の安全性の問題を有しています最後の例では)。