web-dev-qa-db-ja.com

Javaでスーパークラスにジェネリックサブタイプクラス情報を渡す

Java)のイディオムを長い間使用して、(一般に抽象)祖先クラスのメソッドで(非抽象)クラスのクラス情報を使用しました(残念ながら、 tこのパターンの名前を見つけます):

public abstract class Abstract<T extends Abstract<T>> {
    private final Class<T> subClass;

    protected Abstract(Class<T> subClass) {
        this.subClass = subClass;
    }

    protected T getSomethingElseWithSameType() {
        ....
    }
}

そのサブクラスの例:

public class NonGeneric extends Abstract<NonGeneric> {
    public NonGeneric() {
        super(NonGeneric.class);
    }
}

ただし、独自の汎用パラメーターを持つAbstractのサブクラスを定義するのに問題があります。

public class Generic<T> extends Abstract<Generic<T>> {
    public Generic() {
        super(Generic.class);
    }
}

この例はコンパイルできません。同様に、ジェネリック型を指定することはできません。 Generic<T>.classまたはGeneric<?>のようなワイルドカードを使用することもできます。

また、スーパークラスのジェネリック型Tの宣言を? extends Tに置き換えてみましたが、これもコンパイルできません。

このパターンをジェネリック基本クラスで機能させる方法はありますか?

15
errantlinguist

Class<T>のインスタンスを(通常はコンストラクターに)渡す "パターン"(イディオム)は クラスリテラルをランタイムタイプトークンとして を使用しており、ランタイム参照をジェネリック型。それ以外の場合は消去されます。

解決策は、最初にバインドされているトークンクラスを変更することです。

Class<? extends T>

そして、スーパークラスで行ったのと同じような要件をジェネリックサブクラスに課します。具象クラスに型トークンを渡しますが、パラメーターとして適切に入力できます。

これらのクラスは、キャストまたは警告なしでコンパイルされます。

public abstract class Abstract<T extends Abstract<T>> {
    private final Class<? extends T> subClass;

    protected Abstract(Class<? extends T> subClass) {
        this.subClass = subClass;
    }
}

public class NonGeneric extends Abstract<NonGeneric> {
    public NonGeneric() {
        super(NonGeneric.class);
    }
}

public class Generic<T> extends Abstract<Generic<T>> {
    public Generic(Class<? extends Generic<T>> clazz) {
        super(clazz);
    }
}

そして最後に具象クラスで、独自のクラスとして使用法を宣言する場合、どこにもキャストを必要としません。

public class IntegerGeneric extends Generic<Integer> {
    public IntegerGeneric() {
        super(IntegerGeneric.class);
    }
}

キャストなしでGeneric(匿名かどうか)のインスタンスを作成する方法を理解していません。

// can someone fill in the parameters without a cast?
new Generic<Integer>(???);     // typed direct instance
new Generic<Integer>(???) { }; // anonymous

それは可能だとは思いませんが、他の方法で表示されることを歓迎します。

11
Bohemian

ここでの主な問題は、具体的なパラメーター化された型のクラスリテラルがないことです。パラメータ化された型にはランタイム型情報がないため、これは理にかなっています。したがって、生の型を持つクラスリテラルのみを持つことができます。この場合は_Generic.class_です。

参考:

まあ、それは問題ありませんが、_Generic.class_は_Class<Generic>_と互換性のない_Class<Generic<T>>_を提供します。回避策は、それを_Class<Generic<T>>_に変換する方法を見つけることですが、それも直接行うことはできません。 Classのすべてのインスタンス化のファミリを表す_Class<?>_に中間キャストを追加する必要があります。次に、_Class<Generic<T>>_にダウンキャストします。これにより、コンパイラエラーが削除されますが、チェックされていないキャスト警告が表示されます。コンストラクタに@SuppressWarnings("unchecked")で注釈を付けて、警告を削除できます。

_class Generic<T> extends Abstract<Generic<T>> {     
    public Generic() {
        super((Class<Generic<T>>)(Class<?>)Generic.class);
    }
}
_
3
Rohit Jain