繁体   English   中英

当参数化类型通过层次结构传递时,在Java中创建泛型类型的实例吗?

[英]Create instance of generic type in Java when parameterized type passes through hierarchies?

我一直在阅读问题的答案:

在Java中创建泛型类型的实例?

我已经实施了Lars Bohl建议的方法。 我修改了他的代码,如下所示:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class ParameterizedTypeEg<E> {
    public Class<E> getTypeParameterClass() {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }
    private static class StringHome extends ParameterizedTypeEg<String> {
        private String _string;
        StringHome (String string) {
            _string = string;
        }
    }
    public static void main(String[] args)
        throws InstantiationException, IllegalAccessException {
        String str = new StringHome("my string").getTypeParameterClass().newInstance();
        String str2 = new ParameterizedTypeEg<String>().getTypeParameterClass().newInstance();
    }
}

这种方法对于str变量很好用。 然后创建str2,在我看来它是相同的类型(ParameterizedTypeEg <String>,与StringHome基本相同)。 但是,该方法不适用于str2,并且在尝试转换(ParameterizedType)类型时会引发ClassCastException。

即使对于str2,我已经使用String参数化了ParameterizedTypeEg,但getGenericSuperclass()返回的内容与str截然不同。 同样,在方法中,str2将“ this”显示为ParameterizedTypeEg,而对于str,“ this”显示为ParameterizedTypeEg $ StringHome。 我想这是问题的根源。 为什么Java不会也为str2确定了泛型类型?

当参数化类型通过多层层次结构传递时,我遇到了似乎同样的问题? 也就是说,类B <T>包含A <T>,并且我实例化了一个B。在A中,我无法通过使用上述方法确定A的参数化类型来创建String对象。 在包含层次结构的情况下,该方法也会产生异常。 这给我带来了一个问题,因为我希望能够通过多个级别的包含和/或继承传递参数化类型,并在所有情况下都采用相同的方法生成泛型类型的实例。

谢谢,约翰

进行以下更改,您的代码将起作用:

  String str2 = new ParameterizedTypeEg<String>().getTypeParameterClass().newInstance();

   String str2 = new ParameterizedTypeEg<String>(){}.getTypeParameterClass().newInstance();

这将创建ParameterizedTypeEg的匿名子类。 你打电话时

getClass().getGenericSuperclass();

在StringHome上,您将获得所需的ParameterizedTypeEg <java.lang.String>。 如果像您那样创建str2,则该调用仅返回Object,因此将其强制转换为参数化类型的尝试失败:

线程“主”中的异常java.lang.ClassCastException:无法将java.lang.Class强制转换为java.lang.reflect.ParameterizedType

创建匿名子类将使此返回ParameterizedTypeEg <java.lang.String>

这与Google Guice Guave库(btw)的Type Token类中使用的技巧相同。 你写,例如

new TypeToken<List<String>>() {}

并不是

 new TypeToken<List<String>>()  

getClass().getGenericSuperclass(); 为您提供超类的详细信息。 因此,只有在您将参数化超类作为子类时,它才起作用。 如果实例化给定类型参数的参数化超类,则它将无法正常工作。

但是,该方法不适用于str2,并且当我尝试强制转换(ParameterizedType)类型时抛出ClassCastException
...........为什么Java也看不到为str2确定了泛型?

如oracle doc中为ParameterizedType所指定:

ParameterizedType表示参数化类型,例如Collection<String>

但是, ParameterizedTypeEg超类是Object,而不是泛型类型。 因此,由getClass().getGenericSuperclass();返回的Type getClass().getGenericSuperclass(); 是对象本身的Class 将非参数化类型的类型强制转换为ParameterizedType可以为str提供ClassCastException

为什么Java不会也为str2确定了泛型类型?

运行时的对象没有任何类型参数信息。 基本上,要执行.newInstance()创建对象,您需要在运行时拥有类对象,这意味着您必须将其存储在某处以获取它。

它对str起作用的原因恰好是因为String类型存储在类元数据中的某个位置。 类具有元数据,其中包括超类和超接口,如果是内部类则将其封闭,字段和方法的类型(方法的返回类型和参数类型)。 所有这些信息都与泛型一起在运行时存储在字节码中(其原因是Java中的文件可以单独编译,因此在编译一个使用已编译文件中的类的文件时,编译器必须能够查看类文件并查看泛型信息),并且可以在运行时通过对类对象的反射获得。

因此,基本上, StringHome类充当String类对象的“存储”,因为在编译时将String硬编码为StringHome类的类型参数。 因此,在运行时,您可以从此“存储”中获取它。

也就是说,类B <T>包含A <T>,并且我实例化了一个B。在A中,我无法通过使用上述方法确定A的参数化类型来创建String对象。

从上面的讨论中,您可能会发现关键是要在运行时以某种方式“获取” T类的类对象。 创建子类A<T>类将无济于事,因为这样做的目的是,实际类在编译时进行了硬编码,这需要在编译时知道该类。 我们不知道在编译时T是什么类,因此我们不能将其放入元数据中。 我们只能输入“ T”,即类型变量。

因此,问题又回到了需要为type参数表示的类找到某种方式来传递或传输类对象。 您不能再依赖任何基于编译时的机制。 因此,您必须将其传递给其他参数或其他参数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM