Java 嵌套泛型继承
我有以下课程:Java 嵌套泛型继承,java,generics,inheritance,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
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;
}
}
类字段{
私有最终类类型;
公共字段(类类型){
this.type=type;
}
}
选课{
私人最终V值;
私有最终类类型;
公共拾取(V值,类类型){
这个值=值;
this.type=type;
}
}
以及与问题相关的课程:
class PickField<T> extends Field<Pick<T>> {
public PickField(Class<Pick<T>> type) {
super(type);
}
}
类选取字段扩展字段{
公共选取字段(类类型){
超级(型);
}
}
现在,编译器似乎接受了这一点。不幸的是,我不知道/不理解如何创建PickField
的新实例,例如为String
picks创建新实例
这当然不起作用:new PickField(Pick.class)
这是不允许的(我想我明白为什么):new PickField(Pick.class)
那么怎么做呢?或者整个方法是否有某种“味道”?这里有各种各样的问题 首先,正如您所指出的,您无法在编译时获得参数化类型的类,因为泛型类型只编译一个类,而不是每个给定类型参数编译一个类(例如,
Pick.class
idiom不编译,实际上没有意义)
正如您所提到的,仅使用Pick.class
参数化PickField
构造函数将不会再次编译,因为签名不匹配
您可以使用运行时习惯用法来推断正确的Pick
参数,但这会产生另一个问题:由于,您的T
的类型参数在运行时是未知的
因此,您可以通过显式强制转换来参数化构造函数调用,如下所示:
new PickField<String>(
(Class<Pick<String>>)new Pick<String>("", String.class).getClass()
);
new PickField(
(类)新选取(“,String.Class).getClass()
);
。。。编译时会出现“未经检查的强制转换”警告(Type safety:unchecked cast from Class to Class
)
real问题很可能是为什么您需要知道
Pick
类中type
的值 我认为PickField
应该仅使用Pick
实例进行参数化
因此,这样做应该很好:
class PickField<T extends Pick<T>> extends Field<T> {
public PickField(Class<T> c) {
super(c);
}
}
更多信息(与主题相关):
- 有一种方法,但并不完全是一种好方法。
您需要创建如下方法:
public static <I, O extends I> O toGeneric(I input) {
return (O) input;
}
公共静态O通用(I输入){
返回(O)输入;
}
然后,创建对象:
new PickField<String>(toGeneric(Pick.class));
newpickfield(toGeneric(Pick.class));
就像我说的,这不是一个好方法,因为你基本上只是对编译器撒谎,但它是有效的。为了将泛型信息作为参数传递,Class是不够的。你需要一些额外的力量才能做到这一点。请参阅,其中解释了什么是超级类型令牌 简而言之,如果您有以下类别:
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;
}
}
下面是一个示例用法:
TypeToken<Pick<String>> type = new TypeToken<Pick<String>>() {};
PickField<String> pickField = new PickField<>(type);
TypeToken type=new-TypeToken(){};
PickField PickField=新的PickField(类型);
由于
TypeToken
类是抽象的,您需要对它进行子类化(这解释了声明末尾的{}
。这相当于显式强制转换到(类)
并将生成另一个未选中强制类型转换
警告。更不用说,由于它是作为某种通用实用方法呈现的,因此在有人开始发现bug之前,它可能会被误用很多…我不需要知道字段的类型,但需要知道拾取的类型。该字段的类型为拾取
和Pick
将是String
类型。编辑问题以澄清。@JDC.com.“气味”在这里,正如您所提到的,您想知道您的泛型类Pick
在实例范围内的参数化类型。这通常会玷污泛型背后的概念,并可能表明您的设计存在更广泛的问题。感谢您的回答,但我认为在SomeSpecificPick
中有一个错误如果类型参数是类本身,这将给出一些奇怪的循环引用,对吗?不,没有错误。而且这不会给出循环引用-这是所谓的自引用类型,JDK实际上定义了一些这样的类型,例如Integer extensed Comparable
,等等。所以只需尝试一下代码片段并熟悉t他分享了我的链接。:)好的,试过之后似乎有用;)。还有一个额外的问题:如果PickField
看起来像这样会怎么样:PickField扩展字段
,这样我就有了一个选择列表?我无法使super(type)
正确。在这种情况下,您将需要第二个type参数。应该是这样的:PickField扩展字段
您想用它做什么?使用T做一两件特定的事情有几种模式,但是如果你需要做一些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;
}
}
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);