【问题标题】: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() 的参数就是一个例子。

【问题讨论】:

  • 请再次阅读问题
  • 代码在class X 中搜索一个带有int 类型参数的构造函数。 “类”Integer.TYPE 指的是这里的原始整数...
  • 为什么我们需要类文字语法作为参数?字符串“int”参数不够用??
  • 问题是,为什么将int.class 作为参数而不是int?我怀疑设计师觉得在末尾添加.class 可以更清楚地表明这是一个类型文字,而不是缺少括号或输入错误的变量。

标签: java reflection


【解决方案1】:

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

因此,拥有一个代表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 已经解释的那样,名称在运行时是不明确的,因为可以有多个具有相同名称的类,由不同的 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 对象,一个对象包含有关代码内部结构的元信息。但是如果我们对此采取更抽象的观点,我们可以重用该类来表示types:primitives、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 指针?正如一些人已经说过的,离开那部分会导致进一步的混乱和语法错误。事实上,后缀只是延续了编写硬编码类型的模式。

    【讨论】:

      猜你喜欢
      • 2018-09-12
      • 2012-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-21
      • 1970-01-01
      • 2010-12-12
      相关资源
      最近更新 更多