java - 为什么我必须强制转换 Constructor.newInstance 的返回值?
问题描述
根据文档,newInstance() 返回一个 T:https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#newInstance()
因此,如果我有一个返回 T 的方法,为什么我必须将 Constructor.newInstance() 的返回值强制转换为 T?
protected <T extends ParentClass> T getConfig() {
return (T) _childClassConstructor.newInstance();
解决方案
泛型是编译器想象的虚构:运行时 ( java.exe
) 不知道它的含义,不在乎,而且大多数情况下甚至没有这些信息(“擦除”)。是的,公共签名确实带有泛型信息,但就运行时而言,它实际上是一个注释。
因此,它有什么用,真的吗?
嗯,它把事情联系起来。它可以告诉编译器:所以,我有这个方法,它需要 2 个参数。这两个参数可以是任何你喜欢的,但是,它们必须是相同的类型。如果不是,则运行时不会进行核心转储或其他任何操作,只是……这意味着该软件存在错误。因此,如果您看到不符合此规则的代码,请将其标记为错误,以便我知道我需要修复它。
这就是泛型所做的一切。但这很有用,幸运的是!
如果你使用泛型并且它们只出现在一个地方,那基本上是没用的,或者说是hacky。您在这里已经做到了:T 只出现一次,作为此方法的返回类型,其他任何地方都没有。这几乎总是表明代码的作者不了解 java 的泛型做什么。
在这种情况下,您实际上所做的就是将其变成一种巫术魔法任意方法——该方法将调整其返回类型,使其成为调用者希望的任何类型。给定class Parent
and class Joe extends Parent
,class Jane extends Parent
那么这将起作用:
Joe c = getConfig();
怎么getConfig()
知道它的返回类型其实是Joe
? 它没有。它会适应你想要的样子。
这是一件坏事——因为如果你查看代码,它实际上并不是在这里工作的:
class ConfigTool {
private final Class<? extends Parent> c;
public ConfigTool(Class<? extends Parent> c) {
this.c = c;
}
public <T extends ParentClass> T getConfig() {
return (T) c.newInstance();
}
}
和:
ConfigTool tool = new ConfigTool(Jane.class);
Joe joe = tool.getConfig();
只会编译。而且,当然,在运行时会爆炸:该getConfig()
方法最终生成 的实例Jane
,该实例不能分配给 Joe 类型的变量,这表明没有简单的“这是如何修复代码”的答案,因为你'已经写是没有意义的/不可能的。
您可能打算做以下两件事之一:
任何一个:
public ParentClass getConfig() {
return c.newInstance();
}
当然,可能c
是代表 Joe 的 Class 实例,但也可能是 Jane。您的 API 可以做出的唯一承诺是,无论该 Class 实例是什么,它所创建的任何实例都可以分配给 Parent 类型的变量。毕竟,Parent p = new Joe();
是有效的,因为是Parent p = new Jane();
。
没有必要涉及泛型。泛型是一个更高的“元”级别:它将类型系统本身变成一种编程语言,在这里你不需要这样做。
您可能想要的另一种选择是将getConfig()
方法的返回类型链接到该对象的 Class 实例的特定类型:
class ConfigTool<T extends Parent> {
private final Class<T> c;
public ConfigTool(Class<T> c) {
this.c = c;
}
public T getConfig() {
return c.newInstance();
}
}
请注意,在此代码中,绝对不需要该演员表,上面的代码按编写方式编译。泛型也很有用,因为 T 出现在两个地方:Class<T>
构造函数中的参数以及方法的T
返回类型getConfig
(它的出现class Parent<T>
不算数,没有使用它,这就是定义它的存在)。
推荐阅读
- javascript - 通过其他数组查找数组中最接近的值
- python - 尝试从浏览器将图像上传到 s3 时签名不匹配
- c# - 以编程方式创建新 DocumentSet [SharePoint] 时出现异常
- php - PHP:通过身份验证连接到 DHL ELP 网络服务
- ios - 如何切换到另一个与当前共享相同根控制器的视图控制器?
- git - Git push 在 PhpStorm 中不起作用,而在 Tortoise 中 git 工作得很好
- c# - WPF White框架访问自定义控件上的自定义属性
- javascript - 有没有更快的方法在 namesArray 中查找任何名称?
- javascript - 是否可以将列表中的值添加到输入表单字段?
- linux - 在远程服务器上通过 SSH (JSch) 运行 Linux GUI/GTK 应用程序