为啥 getConstructor 反射 API 需要 int.class 参数?

Posted

技术标签:

【中文标题】为啥 getConstructor 反射 API 需要 int.class 参数?【英文标题】:Why int.class argument required with getConstructor reflection api?为什么 getConstructor 反射 API 需要 int.class 参数? 【发布时间】:2015-09-23 07:14:27 【问题描述】:

Class 实例在每个类型的常量池中创建,当特定类 (X) 被加载时。

javac 编译此语法 int.class 以指向 Integer.TYPE 指向的对象。

下面的代码,可以访问class X的parameterise(int)构造函数。

Class c = X.class;
Constructor cons = c.getConstructor(int.class);
cons.newInstance(10);

在上面的代码中,我不明白int.class 参数的作用,在幕后。

为什么getConstructor 不是设计为接受String 类型参数“int”而不是int.classClass.forName() 的参数就是一个例子。

【问题讨论】:

请再次阅读问题 Java: an object of what type is produced if the class literal is applied to the primitive type? 的可能重复项 代码在class X 中搜索一个带有int 类型参数的构造函数。 “类”Integer.TYPE 指的是这里的原始整数... 为什么我们需要类文字语法作为参数?字符串“int”参数不够用?? 问题是,为什么将int.class 作为参数而不是int?我怀疑设计师觉得在末尾添加.class 可以更清楚地表明这是一个类型文字,而不是缺少括号或输入错误的变量。 【参考方案1】:

首先,Class 是一种专用于某种目的的类型。有很多类,我们可以简单地用String 替换它们的实例,如果我们接受一点含糊不清和可能的性能损失,它会起作用。例如。为什么使用数字而不是包含它们的表示的Strings,或者为什么使用enums 而不是它们的名字?因此,拥有专用类型的实例可确保创建、查找、解析或获取该实例所需的任何操作已成功执行。

因此,拥有一个代表int.classClass 对象,您就知道您指的是现有类型,而对于String "int",您不能说这一点。 String 参数不一定引用现有类型——它甚至不必包含有效名称。如果您查找具有两个int 参数的构造函数,传递"int", "int" 将意味着完成验证正确性和查找适当类型的整个工作两次。等等……

而且,由于 Java 编程语言的限制不适用于 JVM,String 是模棱两可的。尚不清楚"int" 是指名为intclass 还是原始类型int。请注意,当您在 ClassLoader 上调用 loadClass("int") 时,始终假定您指的是名为 intclass,因为该方法不适用于查找原始类型。这就是为什么 int.class 被编译为访问 Integer.TYPE 的原因,因为原始类型不能像引用类型一样被查找。

此外,正如 here 已经解释的那样,名称在运行时是不明确的,因为可以有多个具有相同名称的类,由不同的 ClassLoaders 定义。

见JVMS §5.3 “Creation and Loading”:

在运行时,类或接口不仅仅由其名称决定,而是由一对:其二进制名称 (§4.2.1) 及其定义类加载器。

还有JLS §12.2 “Loading of Classes and Interfaces”

行为良好的类加载器维护这些属性:

给定相同的名称,一个好的类加载器应该总是返回相同的类对象。

如果一个类加载器 L1 将类 C 的加载委托给另一个加载器 L2,那么对于任何作为 C 的直接超类或直接超接口出现的类型 T,或者作为 C 中字段的类型,或者作为C 中方法或构造函数的形参类型,或者作为 C 中方法的返回类型,L1 和 L2 应该返回相同的 Class 对象。

恶意类加载器可能会违反这些属性。但是,它不会破坏类型系统的安全性,因为 Java 虚拟机对此进行了防范。

【讨论】:

【参考方案2】:

int.class 代替 Integer.TYPE 是 java 编译器替代的语法糖。简单地说一个Class 对象,一个对象包含有关代码内部结构的元信息。但是如果我们对此采取更抽象的观点,我们可以重用该类来表示typesprimitives、interfaces,甚至void——这不是类型,而是方法的标识符没有 返回值Class 的这种重用有很多好处,就像你的例子一样;考虑以下代码:

public class X 
    public static void main(String args[]) throws Exception 
        X myX = X.class.getConstructor().newInstance();
        X myBigX = X.class.getConstructor(Integer.class).newInstance(0xCEED);
        X mySmallX = X.class.getConstructor(int.class).newInstance(10);
    

    public X() 
        System.out.println("parameterless constructor called");
    

    public X(Integer bigInteger) 
        System.out.println("object integer constructor called");
    

    public X(int smallInteger) 
        System.out.println("primitive integer constructor called");
    

如果反射 API 开发人员已经为每个将 Class 对象作为类型标识符的方法引入,则必须引入第二个方法来处理不完全是 classes 的类型,这会造成不必要的代码重复和不灵活。现在,为什么他们决定将.class 附加到原始类型关键字以引用各自的Class 指针?正如一些人已经说过的,离开那部分会导致进一步的混乱和语法错误。事实上,后缀只是延续了编写硬编码类型的模式。

【讨论】:

以上是关于为啥 getConstructor 反射 API 需要 int.class 参数?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过反射来创建对象?getConstructor()和getDeclaredConstructor()区别?

如何通过反射来创建对象?getConstructor()和getDeclaredConstructor()区别?

反射

Java反射基础

为啥 Java 反射 API 允许我们访问私有和受保护的字段和方法?这不会破坏访问修饰符的目的吗? [复制]

java反射和注解