【问题标题】:How do I use the generic type of this class's generic type (Java)?我如何使用这个类的泛型类型(Java)的泛型类型?
【发布时间】:2015-02-02 05:37:35
【问题描述】:

我有一个带有通用通配符 T 的抽象类 A<T>。A 有两个扩展,

public class A1 extends A<Integer> {...}

public class A2 extends A<String> {...}

现在我有了另一个类,我们称它为 B,它基本上是 A1 或 A2 的集合。所以我把它定义为一个泛型类型的类 T extends A:

public class B<T extends A> {...}

我希望能够在 B 中创建返回 T 的泛型类型的方法。例如,

B<A1> foo = new B<A1>;
foo.get(); // returns Integer, corresponding to A1's generic type

B<A2> bar = new B<A2>;
bar.get(); // returns String, corresponding to A2's generic type

在 Java 中可以做到这一点吗?在声明 B 的方法时,我无法弄清楚要放置什么返回类型(如果我想让 B 返回 T,我会输入 public T get() {...},但我实际上想返回参数化类型 of T)。或者有没有一种模式比我解决这个问题的方式更好地解决了这个问题?

谢谢。

【问题讨论】:

  • TT 的泛型类型,B(如已发布)对于foobarRaw Type(这是另一种说法不是通用的)。

标签: java generics inheritance polymorphism wildcard


【解决方案1】:

您可以为B 声明第二个类型参数,并将其作为A 的参数化

class B<K, T extends A<K>> {
    public K get() {
        // actual implementation
        return null;
    }
}

然后声明你的变量

B<String, A2> var = new B<>();
String ret = var.get();

What is a raw type and why shouldn't we use it?

【讨论】:

  • 如果你不做任何其他事情,你也可以删除参数T
  • 感谢您的快速回答。 @Sotirios,这是一个不好的设计吗?我很好奇您与原始类型的链接。我想我的意思是B&lt;A1&gt; foo = new B&lt;A1&gt; 等(已修复)。这会改变你的答案吗? @immibis,我希望BT 而不是K 相关联,我认为K 的封装信息可能会以某种方式从T 中检索...
  • @tytk A 是一个泛型类型。如果您不对其进行参数化,您将获得其原始版本。所以在你的 sn-p 中,B&lt;T extends A&gt; 使用 A 作为原始类型。
  • 啊,我明白了,谢谢。那么额外的参数是必要的吗?我不能只声明class B&lt;T extends A&lt;K&gt;&gt;吗?
  • @tytk K 用作类型。它必须存在,即。在某处声明。如果是类型变量,需要在泛型类型参数声明列表中声明为类型变量。
【解决方案2】:

我想提供一个示例,可以帮助您更好地理解为什么我们需要两个泛型类型参数。首先,A 及其直接子类的示例声明。

class A<T> {

    private T value;

    public A(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }   
}

class A1 extends A<Integer> {
    A1(Integer i) {
        super(i);
    }
}

class A2 extends A<String> {
    A2(String str) {
        super(str);
    }
}

现在,B 类声明。请注意,它是如何在其定义中引用这两种泛型类型以提供对包装的 A 子类的成员的访问。

public class B<T, X extends A<T>> {

    private X data;

    public B(X data) {
        this.data = data;
    }

    public T get() {
        return data.getValue();
    }

    public static void main(String[] args) {

        B<Integer, A1> foo = new B<Integer, A1>(new A1(10));
        System.out.println(foo.get()); // returns Integer

        B<String, A2> bar = new B<String, A2>(new A2("Ten"));
        System.out.println(bar.get()); // returns String
    }
}

如果你只切换到一个类型参数,你有两个选择。

你要么保持A子类的类型像

public class B<T extends A<?>> {

    private T data;

    public B(T data) {
        this.data = data;
    }

    public Object get() {
        return data.getValue();
    }

    ...
}

或者,getter 的实际 return 类型。

public class B<T> {

    private T value;

    @SuppressWarnings("unchecked")
    public B(A<?> data) {
        this.value = (T) data.getValue();
    }

    public T get() {
        return value;
    }

    ... 
}

注意,这两种解决方案都很脆弱。

一个返回Object 并要求使用instanceof 来正确转换它,而另一个更脆弱的是unchecked 转换。由于您没有同时提供所需的泛型类型,因此限制了类设计并冒着现在获得ClassCastException 的风险。

【讨论】:

  • 谢谢!这是一个非常明显的例子。对我来说,它似乎仍然有点不必要的冗长,但我想这就是 Java 的样子。已经接受了另一个答案,但由于乐于助人而赞成这个答案。
  • 刚刚更新了我的答案来解释:为什么,在 Java 中看起来很冗长,实际上是使类完全类型安全所必需的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-02
  • 1970-01-01
  • 1970-01-01
  • 2019-05-30
  • 2022-06-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多