我有以下课程:
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;?
答案 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 erasure,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
)进行编译。
真实问题可能是您为什么需要知道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()
方法访问。
我稍微修改了您的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
类是抽象的,您需要对其进行子类化(这解释了声明结束时的{}
。