16

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

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);
    }
}

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

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

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

4

3 に答える 3

12

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

私はそれが可能だとは思いませんが、そうでないことが示されることを歓迎します。

于 2013-10-11T12:38:54.767 に答える
3

ここでの主な問題は、具体的なパラメーター化された型のクラス リテラルがないことです。パラメータ化された型には実行時の型情報がないため、これは理にかなっています。したがって、生の型を持つクラス リテラルのみを持つことができます。この場合は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);
    }
}
于 2013-10-11T07:54:01.673 に答える