为什么 getConstructor 反射需要 int.class 参数 api?
Why int.class argument required with getConstructor reflection api?
Class
实例在每个类型的常量池中创建,当特定的 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()
的论点就是一个例子。
int.class
而不是 Integer.TYPE
是 java 编译器替代的语法糖。一个 Class
对象简单地说,一个对象保存关于代码内部结构的元信息。但如果我们对此采取更抽象的观点,我们可以重用 class 来表示 types
:primitive
s,interface
s,甚至 void
-这不是类型,而是具有 no return value 的方法的标识符。 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
指针?正如一些人已经说过的那样,省略该部分会导致进一步的混乱和语法错误。事实上,后缀只是延续了写硬编码类型的模式。
首先,Class
是一种专用于某种目的的类型。有很多 类,我们可以简单地用 String
替换它们的实例,如果我们接受一点歧义和可能的性能损失,它就会起作用。例如。为什么使用数字而不是 String
s 包含他们的代表,或者为什么使用 enum
s 而不是他们的名字?因此,拥有专用类型的实例可确保已成功执行创建、查找、解析或获取该实例所需的任何操作。
所以有一个 Class
对象代表 int.class
,你知道你指的是一个现有的类型,你不能说 String
"int"
. String
参数不一定引用现有类型——它甚至不必包含有效名称。如果您查找具有两个 int
参数的构造函数,传递 "int", "int"
将意味着完成验证正确性和两次查找适当类型的整个工作。等等……
而且,由于 Java 编程语言的限制不适用于 JVM,因此 String
是不明确的。不清楚 "int"
是指一个名为 int
的 class
还是原始类型 int
。请注意,当您在 ClassLoader
上调用 loadClass("int")
时,始终假定您指的是名为 int
的 class
,因为该方法不适合查找基本类型.这就是为什么 int.class
被编译为访问 Integer.TYPE
的原因,因为原始类型不能像引用类型一样被查找。
此外,正如已经解释过的 ,名称在运行时是不明确的,因为可以有多个 类 具有相同的名称,由不同的 ClassLoader
定义。
见JVMS §5.3 “Creation and Loading”:
At run time, a class or interface is determined not by its name alone, but by a pair: its binary name (§4.2.1) and its defining class loader.
还有JLS §12.2 “Loading of Classes and Interfaces”
Well-behaved class loaders maintain these properties:
Given the same name, a good class loader should always return the same class object.
If a class loader L1 delegates loading of a class C to another loader L2, then for any type T that occurs as the direct superclass or a direct superinterface of C, or as the type of a field in C, or as the type of a formal parameter of a method or constructor in C, or as a return type of a method in C, L1 and L2 should return the same Class object.
A malicious class loader could violate these properties. However, it could not undermine the security of the type system, because the Java Virtual Machine guards against this.
Class
实例在每个类型的常量池中创建,当特定的 class(X
) 被加载时。
javac
编译此语法 int.class
以指向 Integer.TYPE
指向的对象。
在代码下方,可以访问 class X
.
int
) 构造函数
Class c = X.class;
Constructor cons = c.getConstructor(int.class);
cons.newInstance(10);
在上面的代码中,我没有理解 int.class
参数的作用,在幕后。
为什么 getConstructor
没有设计为接受 String
类型参数 "int" 而不是 int.class
? Class.forName()
的论点就是一个例子。
int.class
而不是 Integer.TYPE
是 java 编译器替代的语法糖。一个 Class
对象简单地说,一个对象保存关于代码内部结构的元信息。但如果我们对此采取更抽象的观点,我们可以重用 class 来表示 types
:primitive
s,interface
s,甚至 void
-这不是类型,而是具有 no return value 的方法的标识符。 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
指针?正如一些人已经说过的那样,省略该部分会导致进一步的混乱和语法错误。事实上,后缀只是延续了写硬编码类型的模式。
首先,Class
是一种专用于某种目的的类型。有很多 类,我们可以简单地用 String
替换它们的实例,如果我们接受一点歧义和可能的性能损失,它就会起作用。例如。为什么使用数字而不是 String
s 包含他们的代表,或者为什么使用 enum
s 而不是他们的名字?因此,拥有专用类型的实例可确保已成功执行创建、查找、解析或获取该实例所需的任何操作。
所以有一个 Class
对象代表 int.class
,你知道你指的是一个现有的类型,你不能说 String
"int"
. String
参数不一定引用现有类型——它甚至不必包含有效名称。如果您查找具有两个 int
参数的构造函数,传递 "int", "int"
将意味着完成验证正确性和两次查找适当类型的整个工作。等等……
而且,由于 Java 编程语言的限制不适用于 JVM,因此 String
是不明确的。不清楚 "int"
是指一个名为 int
的 class
还是原始类型 int
。请注意,当您在 ClassLoader
上调用 loadClass("int")
时,始终假定您指的是名为 int
的 class
,因为该方法不适合查找基本类型.这就是为什么 int.class
被编译为访问 Integer.TYPE
的原因,因为原始类型不能像引用类型一样被查找。
此外,正如已经解释过的 ClassLoader
定义。
见JVMS §5.3 “Creation and Loading”:
At run time, a class or interface is determined not by its name alone, but by a pair: its binary name (§4.2.1) and its defining class loader.
还有JLS §12.2 “Loading of Classes and Interfaces”
Well-behaved class loaders maintain these properties:
Given the same name, a good class loader should always return the same class object.
If a class loader L1 delegates loading of a class C to another loader L2, then for any type T that occurs as the direct superclass or a direct superinterface of C, or as the type of a field in C, or as the type of a formal parameter of a method or constructor in C, or as a return type of a method in C, L1 and L2 should return the same Class object.
A malicious class loader could violate these properties. However, it could not undermine the security of the type system, because the Java Virtual Machine guards against this.