如何在运行时将 Class<T> 转换为类似于 Class<T 扩展 Foo> 的东西?

How can I cast a Class<T> to something that acts like a Class<T extends Foo> at runtime?

提问人:smithaiw 提问时间:6/12/2017 更新时间:6/12/2017 访问量:435

问:

我有一个如下所示的类:

class FooClassAnalyser<T extends Foo> extends ClassAnalyser<T>

(其中是许多具体实现的抽象基类;并且是专门针对扩展情况的具体实现)。它有一个构造函数,如下所示:ClassAnalyserFooClassAnalyserTFoo

FooClassAnalyser(Class<T> classToAnalyse)

在另一个类中,我有一个静态工厂方法,它根据 的类型调用适当的构造函数:ClassAnalyserclassToAnalyse

static <U> ClassAnalyser<U> constructClassAnalyser(Class<U> classToAnalyse)

我想要的功能是检查是否,然后构造一个,如果是,则返回它。U instanceof FooFooClassAnalyser

但是,我找不到一种方法将其融入 Java 的类型系统。类型擦除意味着我们不能直接做任何聪明的事情。但是,我们作为参数传入的事实使得测试是否通过使用反射来查看:UclassToAnalyseU instanceof Foo

if (Foo.class.isAssignableFrom(classToAnalyse))

我的问题是,与此不同的是,这个“通过反射的实例”对 Java 的类型系统是不可见的。特别是,直接作为参数传递给 的构造函数会因类型不匹配而失败,因为 Java 不知道这实际上是 .instanceofclassToAnalyseFooClassAnalyserclassToAnalyseClass<U extends Foo>

到目前为止,我发现的最好的解决方案是使用未经检查的强制转换来制作一个(它实际上被检查了,但 Java 不知道它被检查了)。这至少使得将它作为参数传递给 成为可能,并得到一个对象作为回报。然而,问题在于这不会转换回 ,因为 Java 不承认强制转换为具有不同的泛型边界并不能改变对象仍然是同一对象(因此仍然是 );换句话说,Java 所能看到的只是一个它无法识别的 ,也是一个 ,因此转换回来需要另一个未经检查的强制转换。结果是编译和运行的代码,但带有许多有关类型安全的警告。classToAnalyseClass<? extends Foo>new FooClassAnalyserFooClassAnalyser<?>ClassAnalyser<U>classToAnalyseClassClass<U>FooClassAnalyser<?>FooClassAnalyser<U>

我尝试过的大多数其他事情都是语法错误(例如,不能直接声明类型的变量;Java 无法正确解析它)。应该注意的是,我实际上在任何时候都没有类型的对象;我正在尝试分析类本身,因此只有对象可以使用。Class<U extends Foo>UClass<U>

是否可以以类型安全的方式编写这样的代码?

Java 泛型 动态 反射 转换

评论

1赞 kaqqao 6/12/2017
就其价值而言,我认为你不能比你已经做的事情做得更好(使用未经检查的演员阵容)。当你用 Java 泛型做任何稍微高级的事情时,它总是会变得粗糙。
3赞 Radiodef 6/13/2017
顺便说一句,您不必使用未经检查的强制转换来 .你可以使用 clazz.asSubclass(Foo.class)。Class<? extends Foo>
1赞 Holger 6/13/2017
@Radiodef:是的,但返回一个已经丢失了知识的 。您可以使用它来安全地构建一个没有警告的,但没有干净的方法来将其返回为......clazz.asSubclass(Foo.class)Class<? extends Foo><U>FooClassAnalyser<? extends Foo>ClassAnalyser<U>
0赞 Radiodef 6/13/2017
@Holger是的,这就是为什么它是旁注。
0赞 Travis J 3/26/2019
这是一个较老的问题,所以我不确定这是否是一个已解决的问题,但是反射包不应该有一个方法,通过传入反射泛型类型来调用您需要的方法或构造函数吗?我知道 c# 可以做到这一点(stackoverflow.com/a/325161/1026459),这就是我问的原因。

答:

0赞 Jacob G. 6/12/2017 #1

我的问题是,与此不同的是,这个“通过反射的实例”对 Java 的类型系统是不可见的。特别是,直接作为参数传递给 的构造函数会因类型不匹配而失败,因为 Java 不知道这实际上是 .instanceofclassToAnalyseFooClassAnalyserclassToAnalyseClass<U extends Foo>

只要您将 a 传递给 .你没有给我们提供太多代码,所以我无法为你写一个明确的解决方案;但是,如果您能找到一种方法来传递而不是 .Class<U>constructClassAnalyserUconstructClassAnalyserClass<U>

static <U> ClassAnalyser<U> constructClassAnalyser(U objectToAnalyse)

要检查其类,可以使用 ;您还可以使用 to 验证 if 返回 ,然后根据需要将其强制转换为其各自的类。objectToAnalyse#getClassinstanceofU extends FooobjectToAnalyse instanceof Footrue

评论

0赞 smithaiw 6/12/2017
a 的全部意义在于它分析了一个类;它不一定具有该类的可用对象。所以这个解决方案是行不通的,这并不能真正回答这个问题。(但是,即使我们确实有一个可用的类对象,我也不相信您可以在我当前的解决方案中摆脱第二个未经检查的强制转换,尽管很容易看出如何摆脱第一个。ClassAnalyser
0赞 Jacob G. 6/12/2017
如果你有这个类,只需使用Class#newInstance
0赞 smithaiw 6/12/2017
这要求类具有可访问的构造函数,并且还需要为构造函数找到适当的参数。此外,构造函数可能会产生副作用。(不过,我仍然不明白它将如何解决最初的问题。
0赞 Jacob G. 6/12/2017
所以做一个吧!无论如何,您只是用它来检查类型
0赞 smithaiw 6/12/2017
(这个对话真的应该被拿去聊天,但我的名声还不够。据我所知,您建议通过为我们可能需要分析的每个类创建一个新的无参数构造函数来解决类型推断问题(这会自动将方法限制为仅分析我们可以编辑的类,并且在逻辑上可以有一个无参数构造函数;您将如何初始化不应为 null 的字段?这似乎非常低效,而且不够普遍,无法解决原始问题。final