获取 Java 中泛型参数的类对象表示
我正在尝试在运行时检索类型参数的 Class
表示,如下所示:
public <E extends Exception> Try<T> onCatch (Consumer<E> onCatch) {
// retrieve `Class<E>`
}
虽然通常由于类型擦除而无法在运行时检索类型,但由于反射实用程序和在字节码中存储关于泛型的元数据应该可以像这个答案中看到的那样<一href="https://stackoverflow.com/q/1901164/16720064">我有同样的问题。
接受的答案如下所示,
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
但对我不起作用,因为 .getActualTypeArguments()[0]
不是 ParameterizedType
的实例,而是 Class
从而导致演员阵容失败。
有什么方法可以可靠地做到这一点吗?我知道我可以手动传递 Class
对该方法的引用,但由于这是一个库,因此能够仅使用泛型类型会更加方便。
I am trying to retrieve the Class<?>
representation of a type parameter at runtime like this:
public <E extends Exception> Try<T> onCatch (Consumer<E> onCatch) {
// retrieve `Class<E>`
}
While normally it is not possible to retrieve the types at runtime because of type erasure, due to reflection utilities and meta-data stored about generics in bytecode it should be possible like seen in this answer to the same question I have.
The accepted answer looks the following
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
but does not work for me, due to .getActualTypeArguments()[0]
not being an instance of ParameterizedType
but Class
and thus failing the cast.
Is there any way to reliably do this? I know I can manually pass a Class<E>
reference to the method but due to this being a library being able to just use the generic type would be much more convenient.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(2)
之所以有效,
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
是因为 this
的超类恰好是一个以参数化类型作为其超类的类。因此,您可以获得该类型的实际类型参数。如果将超类的类型参数写入源文件中,它们将作为元数据存储在类文件中。
但是,在您的情况下,传递给 onCatch
参数的任何内容都不会具有 Consumer
的超类。毕竟,Consumer
不是一个类!您需要使用 getGenericInterfaces
并找到名称以 java.util.function.Consumer
开头的接口。
System.out.println(
// I've assumed the Consumer interface is the first one, to keep it brief
((ParameterizedType)onCatch.getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0]
);
如果调用者像这样调用 onCatch
,这将起作用:
onCatch(new Consumer<RuntimeException>() {
@Override
public void accept(RuntimeException e) {
}
});
匿名类将实现 Consumer
,并且此信息将被写入表示匿名类的类文件中。
但是,如果您使用 lambda:
onCatch((RuntimeException e) -> {});
则仅在与调用者相同的类中生成这样的方法:
private static void lambda$caller$0(RuntimeException e) {
}
并且在运行时, invokedynamic
用于创建一个实现 Consumer
的实例,这是个坏消息:无论出于何种原因,类型参数 RuntimeException
都不是为此实例生成的类的一部分。
现在找到 RuntimeException 的唯一方法就是知道调用者是谁,找到 lambda$caller$0 方法并查看其参数。
也就是说,到目前为止我所写的所有内容几乎都是实现细节,并且我不会在生产代码中使用其中的任何内容。我想说你应该只添加一个 Class
参数:无论如何,
onCatch(RuntimeException.class, e -> {});
它在调用者方面看起来并没有什么不同。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
(声誉不够好,无法回答,但是:)
我认为必须说,这不仅仅是一个“一阶”继承问题。如以下示例所示:
上面的示例将打印
Type[1] { T }
并且不包含任何对运行时类型String
的引用。在 JShell 上(尚未研究“标准”运行时),
g
的类型是 lambda,其直接超类型为Object
。我还没有找到它的类型参数。(not a good enough reputation to answer, but:)
I think it must be said that it's not only a "first-order" inheritance issue. As the following example shows:
The above example will print
Type[1] { T }
and doesn't contain any reference to the runtime-typeString
.On JShell (haven't investigated a "standard" runtime), the type of
g
is a lambda whose direct super type isObject
. I haven't found type parameters on it.