嵌套泛型继承

时间:2016-02-01 12:42:40

标签: java generics inheritance

我有以下课程:

class Field<T> {

  private final Class<T> type;

  public Field(Class<T> type) {
    this.type = type;
  }
}

class Pick<V> {
  private final V value;
  private final Class<V> type;

  public Pick(V value, Class<V> type) {
    this.value = value;
    this.type = type;
  }
}

和问题相关的课程:

class PickField<T> extends Field<Pick<T>> {

  public PickField(Class<Pick<T>> type) {
    super(type);
  }
}

现在这似乎被编译器接受了。不幸的是,我不知道/了解如何创建PickField的新实例,例如String选秀权。

这当然 - 不起作用:
new PickField<String>(Pick.class)

这是不允许的(我想我理解为什么):
new PickField<String>(Pick<String>.class)

那怎么办?或者整个方法是否以某种方式&#34; 闻到&#34;?

4 个答案:

答案 0 :(得分:15)

我认为PickField应仅使用Pick个实例进行参数化。

所以这样做应该没问题:

class PickField<T extends Pick<T>> extends Field<T> {

    public PickField(Class<T> c) {
        super(c);
    }
}

然后,你可以用:

实例化它
PickField<SomeSpecificPick> instance = new PickField<>(SomeSpecificPick.class);

其中SomeSpecificPick定义为:

public class SomeSpecificPick extends Pick<SomeSpecificPick> {

    public SomeSpecificPick(SomeSpecificPick value, Class<SomeSpecificPick> type) {
        super(value, type);
    }
}

更多信息(与主题相关):

答案 1 :(得分:7)

这里有各种各样的问题。

首先,正如您所指出的,您无法在编译时获取参数化类的类,因为只为泛型类型编译了一个类,而不是每个给定类型参数编译一个类(例如Pick<String>.class成语不编译,实际上没有意义)。

再次提到,仅PickField<String>仅的Pick.class 参数化Pick<T>构造函数将无法再次编译,因为签名不匹配。

您可以使用运行时惯用法来推断正确的T参数,但这会产生另一个问题:由于type erasurenew PickField<String>( (Class<Pick<String>>)new Pick<String>("", String.class).getClass() ); 的类型参数在运行时将是未知的。

因此,您可以通过显式转换来参数化构造函数调用,如下所示:

Type safety: Unchecked cast from Class<capture#1-of ? extends Pick> to Class<Pick<String>>

...将使用“未经检查的强制转换”警告(type)进行编译。

真实问题可能是您为什么需要知道Pick课程中ready的价值。

答案 2 :(得分:1)

有一种方法,但并不是一个好方法。 您需要创建一个这样的方法:

public static <I, O extends I> O toGeneric(I input) {
    return (O) input;
}

然后,您创建对象:

new PickField<String>(toGeneric(Pick.class));

就像我说的那样,不是一个好方法,因为你基本上只是欺骗编译器,但是它有效。

答案 3 :(得分:0)

为了将泛型信息作为参数传递,Class<T>是不够的。你需要一些额外的力量来实现这一目标。请参阅this article,其中说明超级型令牌是什么。

简而言之,如果您有以下课程:

public abstract class TypeToken<T> {

    protected final Type type;

    protected TypeToken() {
        Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() {
        return this.type;
    }
}

您可以使用它来存储泛型类型信息,例如Pick<String>.class(这是非法的)。诀窍是使用超类的通用类型信息,可通过Class.getGenericSuperclass()ParameterizedType.getActualTypeArguments()方法访问。

我稍微修改了您的PickFieldPickField类,以便他们使用超类型令牌而不是Class<t>。请参阅修改后的代码:

class Field<T> {

    private final TypeToken<T> type;

    public Field(TypeToken<T> type) {
        this.type = type;
    }
}

class Pick<V> {

    private final V value;

    private final TypeToken<V> type;

    public Pick(V value, TypeToken<V> type) {
        this.value = value;
        this.type = type;
    }
}

class PickField<T> extends Field<Pick<T>> {

    public PickField(TypeToken<Pick<T>> type) {
        super(type);
    }
}

这是一个示例用法:

TypeToken<Pick<String>> type = new TypeToken<Pick<String>>() {};
PickField<String> pickField = new PickField<>(type);

由于TypeToken类是抽象的,您需要对其进行子类化(这解释了声明结束时的{}

相关问题