【问题标题】:How can I cast a Class<T> to something that acts like a Class<T extends Foo> at runtime?如何在运行时将 Class<T> 转换为类似于 Class<T extends Foo> 的东西?
【发布时间】: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&lt;U extends Foo&gt;

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

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

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

【问题讨论】:

  • 对于它的价值,我认为你不能比你已经在做的事情做得更好(使用未经检查的演员表)。当您使用 Java 泛型做任何事情稍微高级一点时,它总是会变得粗糙。
  • 附带说明,您不必对Class&lt;? extends Foo&gt; 使用未经检查的强制转换。你可以使用clazz.asSubclass(Foo.class)
  • @Radiodef:是的,但是clazz.asSubclass(Foo.class) 返回一个Class&lt;? extends Foo&gt;,它丢失了关于&lt;U&gt; 的知识。您可以使用它安全地构造 FooClassAnalyser&lt;? extends Foo&gt; 而不会发出警告,但没有干净的方式将其返回为 ClassAnalyser&lt;U&gt;...
  • @Holger 是的,这就是为什么它是一个旁注。
  • 这是一个较老的问题,所以我不确定这是否是一个已解决的问题,但反射包不应该有一个方法来调用您需要的方法或构造函数,通过传入反映泛型类型?我知道 c# 可以做到这一点(stackoverflow.com/a/325161/1026459),这就是我问的原因。

标签: java generics dynamic reflection casting


【解决方案1】:

我的问题是,与instanceof 不同,这种“通过反射的实例”对 Java 的类型系统不可见。特别是,将classToAnalyse 作为参数直接传递给FooClassAnalyser 的构造函数会失败,因为Java 不知道classToAnalyse 实际上是Class&lt;U extends Foo&gt;

只要您将Class&lt;U&gt; 传递给constructClassAnalyser,就会发生这种情况。您没有为我们提供太多代码,所以我无法为您编写明确的解决方案;但是,如果您能找到将U 传递给constructClassAnalyser 而不是Class&lt;U&gt; 的方法,那就简单多了。

static &lt;U&gt; ClassAnalyser&lt;U&gt; constructClassAnalyser(U objectToAnalyse)

要查看它的类,你可以使用objectToAnalyse#getClass;您还可以使用instanceof 来验证U extends Foo 如果objectToAnalyse instanceof Foo 返回true,然后根据需要将其转换为相应的类。

【讨论】:

  • ClassAnalyser 的全部意义在于它分析了一个类;它不一定有该类的对象可用。所以这个解决方案不起作用,这并不能真正回答这个问题。 (即使我们确实有可用的类的对象,但我不相信您可以在我当前的解决方案中摆脱第二个未经检查的演员,尽管很容易看出如何摆脱第一个。)
  • 如果你有这个类,只需使用Class#newInstance创建一个新实例
  • 这要求类具有可访问的构造函数,并且还需要为构造函数找到适当的参数。此外,构造函数可能有副作用。 (不过,我仍然不知道它会如何解决最初的问题。)
  • 那就做一个吧!你只是用它来检查类型
  • (这个对话真的应该用来聊天,但我还没有足够的声誉。)据我所知,你建议通过创建一个新的 no 来解决类型推断问题-我们可能需要分析的每个类的参数构造函数(这会自动将方法限制为仅分析我们可以编辑的类,并且在逻辑上可能具有无参数构造函数;您将如何初始化 @ 987654339@ 不应该为空的字段?)。这似乎非常低效,也不足以解决最初的问题。
猜你喜欢
  • 1970-01-01
  • 2018-01-03
  • 1970-01-01
  • 1970-01-01
  • 2016-12-12
  • 1970-01-01
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多