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);