【发布时间】:2017-11-13 08:48:23
【问题描述】:
我有一个如下所示的类:
class FooClassAnalyser<T extends Foo> extends ClassAnalyser<T>
(其中ClassAnalyser 是许多具体实现的抽象基类;FooClassAnalyser 是专门针对T 扩展Foo 的情况的具体实现)。它有一个如下所示的构造函数:
FooClassAnalyser(Class<T> classToAnalyse)
在另一个类中,我有一个 ClassAnalysers 的静态工厂方法,它根据 classToAnalyse 的类型调用适当的构造函数:
static <U> ClassAnalyser<U> constructClassAnalyser(Class<U> classToAnalyse)
我想要的功能是检查是否U instanceof Foo,然后构造一个FooClassAnalyser,如果是则返回。
但是,我找不到一种方法来适应 Java 的类型系统。类型擦除意味着我们不能直接用U 做任何聪明的事情。然而,我们将classToAnalyse 作为参数传入这一事实使得我们可以通过反射来测试U instanceof Foo:
if (Foo.class.isAssignableFrom(classToAnalyse))
我的问题是,与instanceof 不同,这种“通过反射的实例”对 Java 的类型系统不可见。特别是,将classToAnalyse 作为参数直接传递给FooClassAnalyser 的构造函数会失败,因为Java 不知道classToAnalyse 实际上是Class<U extends Foo>。
到目前为止,我发现的最佳解决方案是使用未经检查的强制转换使 classToAnalyse 成为 Class<? extends Foo>(实际上已检查,但 Java 不知道已检查)。这至少可以将其作为参数传递给new FooClassAnalyser,并获得FooClassAnalyser<?> 对象作为回报。然而,问题在于它不会再转换回ClassAnalyser<U>,因为Java 无法识别将classToAnalyse 转换为具有不同的泛型绑定并不会改变Class 对象这一事实仍然是同一个对象(因此仍然是Class<U>);换句话说,Java 所能看到的只是一个它无法识别的FooClassAnalyser<?>,也是一个FooClassAnalyser<U>,因此转换回来需要另一个未经检查的强制转换。结果是编译和运行的代码,但有许多关于类型安全的警告。
我尝试过的大多数其他事情都是语法错误(例如,Class<U extends Foo> 类型的变量不能直接声明;Java 无法正确解析)。应该注意的是,我实际上在任何时候都没有U 类型的对象。我正在尝试分析类本身,因此只有 Class<U> 对象可以使用。
是否可以以类型安全的方式编写这样的代码?
【问题讨论】:
-
对于它的价值,我认为你不能比你已经在做的事情做得更好(使用未经检查的演员表)。当您使用 Java 泛型做任何事情稍微高级一点时,它总是会变得粗糙。
-
附带说明,您不必对
Class<? extends Foo>使用未经检查的强制转换。你可以使用clazz.asSubclass(Foo.class)。 -
@Radiodef:是的,但是
clazz.asSubclass(Foo.class)返回一个Class<? extends Foo>,它丢失了关于<U>的知识。您可以使用它安全地构造FooClassAnalyser<? extends Foo>而不会发出警告,但没有干净的方式将其返回为ClassAnalyser<U>... -
@Holger 是的,这就是为什么它是一个旁注。
-
这是一个较老的问题,所以我不确定这是否是一个已解决的问题,但反射包不应该有一个方法来调用您需要的方法或构造函数,通过传入反映泛型类型?我知道 c# 可以做到这一点(stackoverflow.com/a/325161/1026459),这就是我问的原因。
标签: java generics dynamic reflection casting