为啥 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.class
? Class.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
替换它们的实例,如果我们接受一点含糊不清和可能的性能损失,它会起作用。例如。为什么使用数字而不是包含它们的表示的String
s,或者为什么使用enum
s 而不是它们的名字?因此,拥有专用类型的实例可确保创建、查找、解析或获取该实例所需的任何操作已成功执行。
因此,拥有一个代表int.class
的Class
对象,您就知道您指的是现有类型,而对于String
"int"
,您不能说这一点。 String
参数不一定引用现有类型——它甚至不必包含有效名称。如果您查找具有两个int
参数的构造函数,传递"int", "int"
将意味着完成验证正确性和查找适当类型的整个工作两次。等等……
而且,由于 Java 编程语言的限制不适用于 JVM,String
是模棱两可的。尚不清楚"int"
是指名为int
的class
还是原始类型int
。请注意,当您在 ClassLoader
上调用 loadClass("int")
时,始终假定您指的是名为 int
的 class
,因为该方法不适用于查找原始类型。这就是为什么 int.class
被编译为访问 Integer.TYPE
的原因,因为原始类型不能像引用类型一样被查找。
此外,正如 here 已经解释的那样,名称在运行时是不明确的,因为可以有多个具有相同名称的类,由不同的 ClassLoader
s 定义。
见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
对象,一个对象包含有关代码内部结构的元信息。但是如果我们对此采取更抽象的观点,我们可以重用该类来表示types
:primitive
s、interface
s,甚至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
对象作为类型标识符的方法引入,则必须引入第二个方法来处理不完全是 class
es 的类型,这会造成不必要的代码重复和不灵活。现在,为什么他们决定将.class
附加到原始类型关键字以引用各自的Class
指针?正如一些人已经说过的,离开那部分会导致进一步的混乱和语法错误。事实上,后缀只是延续了编写硬编码类型的模式。
【讨论】:
以上是关于为啥 getConstructor 反射 API 需要 int.class 参数?的主要内容,如果未能解决你的问题,请参考以下文章
如何通过反射来创建对象?getConstructor()和getDeclaredConstructor()区别?
如何通过反射来创建对象?getConstructor()和getDeclaredConstructor()区别?