繁体   English   中英

嵌套泛型继承

[英]Nested 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 picks。

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

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

那怎么办呢? 或者整个方法是否以某种方式“ ”?

我认为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);
    }
}

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

这里有各种各样的问题。

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

再次提到, 使用Pick.class参数化PickField<String>构造函数将不会再次编译,因为签名不匹配。

您可以使用运行时惯用法来推断正确的Pick<T>参数,但这会产生另一个问题:由于键入擦除T的类型参数在运行时将是未知的。

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

new 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 safety: Unchecked cast from Class<capture#1-of ? extends Pick> to Class<Pick<String>> )。

真正的问题可能是你为什么需要知道Pick类中的type值。

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

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

然后,您创建对象:

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

就像我说的,不是一个好方法,因为你基本上只是骗算编译器,但它的工作原理。

为了将泛型信息作为参数传递, Class<T>是不够的。 你需要一些额外的力量来实现这一目标。 请参阅这篇文章 ,其中解释了超类型令牌的含义。

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

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类是抽象的,因此需要对其进行子类化(这解释了声明结尾处的{}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM