[英]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()
方法访问它们。
我稍微修改了Pick
, Field
和PickField
类,以便它们使用超类型令牌而不是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.