首页 > 解决方案 > 为什么我必须强制转换 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

解决方案


泛型是编译器想象的虚构:运行时 ( java.exe) 不知道它的含义,不在乎,而且大多数情况下甚至没有这些信息(“擦除”)。是的,公共签名确实带有泛型信息,但就运行时而言,它实际上是一个注释。

因此,它有什么用,真的吗?

嗯,它把事情联系起来。它可以告诉编译器:所以,我有这个方法,它需要 2 个参数。这两个参数可以是任何你喜欢的,但是,它们必须是相同的类型。如果不是,则运行时不会进行核心转储或其他任何操作,只是……这意味着该软件存在错误。因此,如果您看到不符合此规则的代码,请将其标记为错误,以便我知道我需要修复它。

这就是泛型所做的一切。但这很有用,幸运的是!

如果你使用泛型并且它们只出现在一个地方,那基本上是没用的,或者说是hacky。您在这里已经做到了:T 只出现一次,作为此方法的返回类型,其他任何地方都没有。这几乎总是表明代码的作者不了解 java 的泛型做什么。

在这种情况下,您实际上所做的就是将其变成一种巫术魔法任意方法——该方法将调整其返回类型,使其成为调用者希望的任何类型。给定class Parentand class Joe extends Parentclass 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>不算数,没有使用它,这就是定义它的存在)。


推荐阅读