web-dev-qa-db-ja.com

なぜJavaはあなたをコレクションにキャストさせますか?

私は単純な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コンパイラが非互換型エラーを返さないのはなぜですか?

FooCollectionを実装していません。現在のFooクラスのシグネチャを考えれば、これはCollectionにはなり得ないので、互換性のない型エラーが予想されます。

65
istovatis

コレクションクラスであるためではなく、インターフェイスであるためです。 Fooはそれらを実装しませんが、そのサブクラスは実装できます。したがって、これらのメソッドはサブクラスに対して有効である可能性があるため、コンパイル時エラーではありません。 runtimeでは、thisがこれらのインターフェイスを実装するクラスのものではない場合、当然ランタイムエラーです。

List<String>ArrayList<String>に変更すると、FooサブクラスはListを実装できますが、拡張できないため、そのためのコンパイラー時間エラーも発生します。 ArrayListFooはしないため)。同様に、Foofinalを作成すると、コンパイラーはインターフェイスキャストのエラーを生成します。これは、Fooがサブクラスを持つことができず、これらのインターフェースを実装しないでください)。

125
T.J. Crowder

コンパイラは、関係が不可能であることを確実に確立できない限り、コードがインタフェースに型をキャストするのを妨げることはありません。

ターゲット型がインタフェースの場合は、Fooを拡張するクラスがMap<String, String>を実装できるため、それは意味があります。ただし、これはFoofinalとしてのみ機能することに注意してください。クラスをfinal class Fooで宣言した場合、そのキャストは機能しません。

ターゲット型がクラスの場合、コンパイラはFooHashMapの関係が不可能であることを確実に認識しているため、この場合は失敗します((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を実装しなければならず、そうでなければコンパイル時エラーが発生します。

引用符で囲まれたテキストの太字斜体コメントに注意してください。

47
ernest_k