私は単純なfoo
クラスを持っていて、コンパイラエラーなしにコレクションインタフェース(Map
またはList
)にキャストすることができます。 Foo
クラスは、インタフェースを実装したり、他のクラスを拡張したりしないことに注意してください。
public class Foo {
public List<String> getCollectionCast() {
return (List<String>) this; // No compiler error
}
public Map<String, String> getCollection2Cast() {
return (Map<String, String>) this; // No compiler error
}
public Other getCast() {
return (Other)this; // Incompatible types. Cannot cast Foo to Other
}
public static class Other {
// Just for casting demo
}
}
Foo
クラスをコレクションにキャストしようとすると、Javaコンパイラが非互換型エラーを返さないのはなぜですか?
Foo
はCollection
を実装していません。現在のFoo
クラスのシグネチャを考えれば、これはCollection
にはなり得ないので、互換性のない型エラーが予想されます。
コレクションクラスであるためではなく、インターフェイスであるためです。 Foo
はそれらを実装しませんが、そのサブクラスは実装できます。したがって、これらのメソッドはサブクラスに対して有効である可能性があるため、コンパイル時エラーではありません。 runtimeでは、this
がこれらのインターフェイスを実装するクラスのものではない場合、当然ランタイムエラーです。
List<String>
をArrayList<String>
に変更すると、Foo
サブクラスはList
を実装できますが、拡張できないため、そのためのコンパイラー時間エラーも発生します。 ArrayList
(Foo
はしないため)。同様に、Foo
final
を作成すると、コンパイラーはインターフェイスキャストのエラーを生成します。これは、Foo
がサブクラスを持つことができず、これらのインターフェースを実装しないでください)。
コンパイラは、関係が不可能であることを確実に確立できない限り、コードがインタフェースに型をキャストするのを妨げることはありません。
ターゲット型がインタフェースの場合は、Foo
を拡張するクラスがMap<String, String>
を実装できるため、それは意味があります。ただし、これはFoo
がfinal
としてのみ機能することに注意してください。クラスをfinal class Foo
で宣言した場合、そのキャストは機能しません。
ターゲット型がクラスの場合、コンパイラはFoo
とHashMap
の関係が不可能であることを確実に認識しているため、この場合は失敗します((HashMap<String, String>) this
を試してください)。
参考までに、これらの規則は JLS-5.5.1 (T =ターゲット・タイプ - Map<String, String>
、S =ソース・タイプ - Foo
)で説明されています。
T [target type]がインタフェース型の場合:
Sが最終クラスでない場合(8.1.1)、XのスーパータイプXとSのスーパータイプYが存在する場合、XとYの両方は明らかに異なるパラメータ化されたタイプであり、Xの消去はとYが同じ場合、コンパイル時エラーが発生します。
それ以外の場合、キャストはコンパイル時には常に有効です(SがTを実装していなくても、Sのサブクラスになる可能性があるため)。Sが最終クラス(8.1.1)である場合、SはTを実装しなければならず、そうでなければコンパイル時エラーが発生します。
引用符で囲まれたテキストの太字斜体コメントに注意してください。