将通用子类类信息传递给Java中的超类

时间:2013-10-11 07:32:19

标签: java generics inheritance abstract-class

我长期以来在Java中使用一个习惯用法在其(通常是抽象的)祖先类(es)的方法中使用(非抽象)类的类信息(遗憾的是我找不到这个的名字)图案):

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,但这也不可编译。

有没有办法让这个模式与泛型基类一起使用?

3 个答案:

答案 0 :(得分:11)

传递Class<T>实例(通常是构造函数)的“模式”(惯用语)是使用Class Literals as Runtime-Type Tokens,用于保持对泛型类型的运行时引用,否则擦除。

解决方案首先是将绑定的令牌类更改为:

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

我认为这不可行,但我欢迎以其他方式展示。

答案 1 :(得分: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);
    }
}

答案 2 :(得分:-1)

Class<T> subClass论证中没有必要。变化:

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

为:

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

一切都会编译。