【问题标题】:How to keep generic type of nested generics with class tokens如何使用类标记保持嵌套泛型的泛型类型
【发布时间】:2016-05-15 08:00:01
【问题描述】:

Java 中解决类型擦除的标准方法是将类标记传递给构造函数。例如,我们可以像这样定义一个通用属性类:

class Prop<T> {
    public Prop(Class<T> type) {
        this.type = type;
    }
    Class<T> type;
    T t;
}

class IntProp extends Prop<Integer> {
    public IntProp() {
        super(Integer.class);
    }
}

但是,如果我现在想使用另一个泛型类型参数(例如列表)并保留其泛型类型,该怎么办。我本来希望这样做:

class ListProp<J> extends Prop<ArrayList<J>> {
    Class<J> subtype;
    public ListProp(Class<J> type) {
        super(ArrayList<J>.class);
        subtype = type;
    }
}

class IntListProp extends ListProp<Integer> {
    public IntListProp() {
        super(Integer.class);
    }
}

当然 super(ArrayList&lt;J&gt;.class) 不会编译,super(ArrayList.class) 也不会。解决这个问题的最佳方法是什么?

【问题讨论】:

    标签: java generics type-erasure nested-generics


    【解决方案1】:

    编译ListProp 类所需的泛型功夫是这一行:

    super((Class<List<T>>)(Class<?>)List.class); // compiles
    

    尝试直接从List.class 转换为Class&lt;List&lt;T&gt;&gt;

    super((Class<List<T>>)List.class); //compile error
    

    导致编译错误:

    不可转换的类型;无法将 'java.lang.Class' 转换为 'java.lang.Class>

    但如果您首先将其转换为类型化类Class&lt;?&gt;,尽管是未知类型,您可以然后将其转换为所需的类型化类。

    完整的可编译ListProp 类是然后。

    class ListProp<T> extends Prop<List<T>> {
        Class<T> subtype;
        public ListProp(Class<T> type) {
            super((Class<List<T>>)(Class<?>)List.class); // magic double cast
            subtype = type;
        }
    }
    

    如果您需要特殊代码来创建/返回列表,您可以考虑的其他内容是 Prop 上的 t 的类型化 getter:

    public T getT() {
        return t;
    }
    

    然后您可以在 ListProp 中协变覆盖以返回 List&lt;T&gt;

    @Override
    public List<T> getT() {
        return Arrays.asList(subtype.newInstance()); // or whatever
    }
    

    需要注意的是,如果你的实现使用类,你只需要类标记,这在你的示例代码中没有显示。如果您实际上不使用类令牌,则可以让类型推断为您进行输入。

    【讨论】:

    • 是的,需要我拥有的类型令牌的 getter 和代码,只是为了简单起见,我没有展示它。哇,那个双重演员真的是一些泛型功夫,但效果很好。感谢您提出这个解决方案!
    【解决方案2】:

    小介绍

    我知道一种解决问题的方法。类型擦除用于在编译后擦除类型,因为它们不需要运行。这可能就是为什么您不能只使用 type 访问类 List 的原因。 List 本身使用泛型,因此为此提供一个类是没有意义或不可能的,因为在您的情况下, T 实际上是列表元素的类型。该类实际上是 ArrayList。您正在寻找的是您正在使用的集合类型和您正在使用的元素类型。我对您的代码进行了一些更改,以便您可以接受这两种类型的构造函数。


    1. T 现在是集合中元素的类型
    2. J 现在是 Collection 类型

    代码

      class ListProp<T, J> extends Prop<T, J> {
                Class<T> subtype;
                Class<J> subtypeElementList:
    
                public ListProp(Class<T> typeElement, Class<J> typeList) {
                    super(typeElement, typeList);
                    subtype = typeElement;
                    subtypeElementList = typeList;
                }
    
            }
    
            class IntListProp extends ListProp<Integer, ArrayList> {
                public IntListProp() {
                    super(Integer.class, ArrayList.class);
                }
            }
    
            class Prop<T, J> {
                // TODO: Maybe here the elements again? Depends on what you want to do...
                //
                // or maybe just use the integer as you had previously.
                public Prop(Class<T> integerClass, Class<J> arrayListClass) {
    
                }
            }
    

    【讨论】:

    • 问题是现在 Prop 需要 2 个泛型参数,即使它仅用于非泛型类型,例如我的示例中的 IntProp。我不想将 List 的泛型类型参数泄漏到 Prop 中。
    • 我知道,但我有一种感觉,你想要的并不是那么简单。仅仅因为您示例的类是 ArrayList。 ArrayList 不是一个类。它只是一个泛型类型。因此,据我所知,也许另一种方法是将两个参数封装成一个并将其传递给 Prop 构造函数,也就是说,如果您在这里寻找的只有一个参数。
    • 即使使用封装类型,它也会改变 prop 的工作方式,而我希望嵌套类型 J 只接触 ListProp。奇怪的是, super(ArrayList.class) 对运行时非常有意义,因为 super(ArrayList.class) 无论如何都会被类型擦除为 ArrayList.class。只是这里的编译器介于我和字节码之间,所以我怀疑有办法做到这一点。
    • 我刚才想到的事情是你可以将 T 传递给 Prop。你已经知道我认为它是一个 ArrayList,所以可能不需要在 prop 中包含 J,然后你只有一种类型进入。这是解决这个问题的方法吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-21
    • 2019-03-10
    • 1970-01-01
    相关资源
    最近更新 更多