尽管 Java 具有类型擦除功能,但仍获得 T.class

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2225979/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:56:38  来源:igfitidea点击:

Getting T.class despite Java's type-erasure

javagenericstype-erasure

提问by Kaleb Pederson

I'm trying to bind an interface to its implementation as read from a configuration file so that I can feed it to my IoC container. Here's roughly what I'm trying to do:

我正在尝试将接口绑定到从配置文件读取的实现,以便我可以将它提供给我的 IoC 容器。这大致是我想要做的:

public class PropertyImplementationBinder<T> {
    // ...
    public Class getInterfaceClass() {
        return T.class; // OR Class<T>, note T is not newable
    }
    public Class getImplementationClass() {
        return /* read config file to get implementation class */;
    }
}

Is it somehow possible to get T.class?

是否有可能获得T.class

采纳答案by Thilo

You need to explicitly pass the class into the constructor (and store it yourself).

您需要将类显式传递给构造函数(并自己存储)。

private final Class<T> clazz;

PropertyImplementationBinder(Class<T> clazz){
    this.clazz = clazz;
}

public Class<T> getInterfaceClass() {
    return clazz;
}

回答by cletus

No it's not possible.

不,这是不可能的。

The only exception to Java's type erasure is that via reflection you can find out the parameterized type via reflection on a class's fields.

Java 类型擦除的唯一例外是通过反射,您可以通过对类字段的反射找出参数化类型。

回答by kloffy

You can get the actual type arguments for a generic superclass of a class. This blog postexplores the possibilities presented by this, including a nice little trick using trivial anonymous inner classes. To quote directly:

您可以获得类的泛型超类的实际类型参数。这篇博文探讨了由此带来的可能性,包括使用琐碎的匿名内部类的一个不错的小技巧。直接引用:

It turns out that while the JVM will not track the actual type arguments for instances of a generic class, it does track the actual type arguments for subclassesof generic classes. In other words, while a new ArrayList<String>()is really just a new ArrayList()at runtime, if a class extends ArrayList<String>, then the JVM knows that Stringis the actual type argument for List's type parameter.

事实证明,虽然 JVM 不会跟踪泛型类实例的实际类型参数,但它会跟踪泛型类的子类的实际类型参数。换句话说,虽然 a在运行时new ArrayList<String>()实际上只是 a new ArrayList(),但如果一个类扩展了ArrayList<String>,那么 JVM 就知道它String是 的类型参数的实际List类型参数。

回答by Richard Gomes

Contrary to what is widely accepted and rarely known type erasure can be avoided, which means that the callee do have the ability to know which generic parameters were employed during the call.

与广泛接受且鲜为人知的类型擦除相反,可以避免类型擦除,这意味着被调用者确实有能力知道在调用期间使用了哪些泛型参数。

Please have a look at: Using TypeTokens to retrieve generic parameters

请查看: 使用 TypeTokens 检索泛型参数

The article also talks about the experiences of our users with the technique. In a nutshell, we ended up falling back to the...

这篇文章还讨论了我们的用户使用该技术的经验。简而言之,我们最终回到了......

Conventional and widely used technique: "Pass class types in constructors"

常规和广泛使用的技术:“在构造函数中传递类类型”

回答by durilka

Btw. The example static method getType in article from @Richard Gomes has two errors. it should go like this:

顺便提一句。@Richard Gomes 文章中的示例静态方法 getType 有两个错误。它应该是这样的:

static public Class<?> getType(final Class<?> klass, final int pos) {
    // obtain anonymous, if any, class for 'this' instance
    final Type superclass = klass.getGenericSuperclass();

    // test if an anonymous class was employed during the call
    if ( !(superclass instanceof ParameterizedType) ) {
            throw new RuntimeException("This instance should belong to an anonymous class");
    }

    // obtain RTTI of all generic parameters
    final Type[] types = ((ParameterizedType) superclass).getActualTypeArguments();

    // test if enough generic parameters were passed
    if ( pos >= types.length ) {
            throw new RuntimeException(String.format("Could not find generic parameter #%d because only %d parameters were passed", pos, types.length));
    }

    if (!(types[pos] instanceof Class<?>)) {
            throw new RuntimeException("Generic type is not a class but declaration definition(all you get is \"[T]\") " + types[pos]);
    }
    // return the type descriptor of the requested generic parameter
    return (Class<?>) types[pos];
}

Unfortunately it's still not the magic bullet because it works if you have in code explicitly

不幸的是,它仍然不是灵丹妙药,因为如果您在代码中明确地使用它,它就可以工作

getType(new SomeObject<String>(){}.class, 0) // you get String.class

but if you call this on something like

但如果你把它称为类似的东西

getType(new SomeObject<T>(){}.class, 0) // you get T as TypeVariable<D> and not actuall class of it

Just name T.

就叫T。