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 Literals 作为 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

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

于 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 回答
-1

没有必要Class<T> subClass争论。改变:

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

到:

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

一切都会编译。

于 2013-10-11T08:04:23.027 回答