【问题标题】:Passing generic subtype class information to superclass in Java将通用子类型类信息传递给Java中的超类
【发布时间】:2013-10-19 05:01:17
【问题描述】:

我长期以来一直使用 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);
    }
}

这个例子是不可编译的;同样,不可能使用例如指定泛型类型。 Generic&lt;T&gt;.class 甚至可以使用像 Generic&lt;?&gt; 这样的通配符。

我还尝试将超类中泛型类型T 的声明替换为? extends T,但这也无法编译。

有什么方法可以让这种模式与通用基类一起使用?

【问题讨论】:

  • I can't find the name of this pattern:听起来像它的Strategy pattern
  • 这不是一种模式,它是java中的一个习惯用法,可能是为了规避糟糕的泛型实现......而且不知道它是如何被调用的。
  • 我认为您正在寻找curiously recurring template pattern - 即“自我类型”。 Java 不支持 self 类型,它 can only be approximated.
  • "我长期使用设计模式" Java 中没有这样的模式。你正在做的事情可以用public abstract class Abstract&lt;T&gt;
  • 我已经在我的答案中重写了很多代码 - 现在有一个无演员表的解决方案可供您使用。

标签: java generics inheritance abstract-class


【解决方案1】:

传递Class&lt;T&gt; 实例(通常传递给构造函数)的“模式”(惯用语)使用Class Literals as Runtime-Type Tokens,并用于保持对泛型类型的运行时引用,否则将被删除。

解决办法是先把绑定的token类改成:

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

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

【讨论】:

  • 我想到了这种方式,但是无论如何你必须在将参数传递给构造函数时执行强制转换。我想演员是不可避免的。如果你能找到一些没有演员的方法,那将是可观的。 :)
  • @RohitJain 如果您将具体的 impl 声明为它们自己的类,则不需要强制转换 - 请参阅编辑。我认为您需要对匿名 impl 或 Generic 本身的实例进行强制转换。
  • 你的意思是extends Generic&lt;GenericFoo&gt;
  • @RohitJain 现在可以编译了。更改的是子类的构造函数的参数类型为Class&lt;Generic&lt;Integer&gt;&gt;。所以你可以声明一个没有任何强制转换的子类。
  • @RohitJain 最后,我已经破解了:)
【解决方案2】:

您在这里遇到的主要问题是,具体参数化类型没有类文字。这是有道理的,因为参数化类型没有任何运行时类型信息。因此,您只能使用原始类型的类文字,在本例中为 Generic.class

参考:

好吧,这很好,但是Generic.class 给你一个Class&lt;Generic&gt;,它与Class&lt;Generic&lt;T&gt;&gt; 不兼容。一种解决方法是找到一种将其转换为Class&lt;Generic&lt;T&gt;&gt; 的方法,但您也不能直接这样做。您必须向Class&lt;?&gt; 添加一个中间转换,它代表Class 的所有实例化家族。然后向下转换为Class&lt;Generic&lt;T&gt;&gt;,这将消除编译器错误,但您将收到未经检查的转换警告。您可以使用@SuppressWarnings("unchecked") 注释构造函数以消除警告。

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

【讨论】:

  • @Bohemian 即使在今天之前我也没有发现自己使用过它。 IMO,我避免构建如此复杂的通用结构,这将迫使我做这样的事情。换句话说,我喜欢善待我的程序员伙伴。 ;)
  • fyi 我想我想出了一个更干净的方法 - 看看我的答案
【解决方案3】:

Class&lt;T&gt; subClass 参数中不需要。变化:

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

到:

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

一切都会编译。

【讨论】:

  • 这在大多数情况下都有效,但在我的具体情况下,我还使用Class 对象动态转换为T,这也很丑陋。我使用其他语言的次数越多,我就越意识到 Java 泛型的混乱程度。
  • 不要过度使用泛型,这不是灵丹妙药。他们只是帮助发展,仅此而已。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-11
  • 1970-01-01
  • 2011-10-16
  • 1970-01-01
  • 2011-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多