java.lang.Class 定义了仅适用于某种类型的类的多种方法,例如:
getComponentType:仅与数组相关getEnumConstants:仅与枚举相关那么为什么还有,例如,没有ArrayClass哪个定义getComponentType方法呢?
这似乎或多或少是一种设计选择。由于我没有设计这种语言,因此我无法肯定地回答它,但我会尝试解释其潜在原因。
那么为什么没有 ArrayClass 来定义 getComponentType 方法呢?
理解这一点的技巧是,该类java.lang.Class并不直接等同于您正在编码的类。这个特定的类仅用于表示运行时创建的类(例如:用于反射的使用)。
根据该类的 Java 文档(此处来自 Java 7):
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组还属于一个类,该类反映为由具有相同元素类型和维数的所有数组共享的 Class 对象。原始 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
如果您编写代码,那么您正在编写自定义类,这意味着您创建的代码就是该类。如果您创建一个名为ArrayClass 的类,那么这就是您的类。该类java.lang.Class只能在运行时用于分析该特定对象的类。
对于Java中的设计,又增加了两层复杂性。
第一:你可以分析每个对象,它是java.lang.Object的子类型。该方法getClass在这里定义。这意味着您能够内省任何对象的任何特定细节。例如,如果对象的特定类型不是枚举,则该类在调用时返回 null getEnumConstants。如果您有 的特定子类型java.lang.Class,则必须在自省之前强制转换实例,这会使使用起来更加烦人。
第二:java.lang.Class代表您的类型的对象是在ClassLoader中惰性创建的。在引用特定类型时,类加载器会检查该类是否已加载,如果没有,则加载它。然后它创建 java.lang.Class-Objects,它代表这个特定的类型。然后可以使用该类对象来创建您的类型的实例。
如果 java 语言对 java.lang.Class 有不同的子类型,则 ClassLoader 必须根据需要实例化该类的正确子类型。如果您能够创建 java.lang.Class 的自定义子类型,那么这很快就会失控。类加载器如何知道哪个类实例连接到您的类型?您是否必须编写特定的类实例并以某种方式标记您创建的类型才能使用该类实例?你可以想象这样的事情:
public class MyType extends ... implements ... type MyClassInstance
Run Code Online (Sandbox Code Playgroud)
但是,如何在自定义 java.lang.Class 实例中填充自定义字段呢?这些复杂性可能是 java.lang.Class 具有泛型类型(表示连接的类型)的原因。尽管对于这些复杂情况有一百万种可能的解决方案,但对于可用性和鲁棒性仍然可能存在争议。
正如 Oracle 在Java 编程语言的设计目标中指出的那样:
为满足这些需求而出现的系统很简单,因此大多数开发人员都可以轻松编程;熟悉,让当前的开发者能够轻松学习Java编程语言;...
尽管人们希望在语言中看到类似的东西,但这一功能会使事情变得更加复杂,因为它允许开发人员改变newInstance例如方法的行为。您可以在此方法中引入异常,人们可能会认为构造函数抛出了异常,即使它没有抛出。这与为什么 Java 不提供运算符重载? 的方向相同(或相似)。,这样,您基本上就覆盖了new关键字。
该类当前是最终的(可能是因为它是核心语言构造)。这禁止 java.lang.Class 的子类型。如果类应该接收子类型,则它必须释放最终值,这意味着任何人都可以覆盖类的任何特定细节。调用getClass可能会产生任何未知的类型,它可以做任何事情。
这样,我们现在只有类的特定子类型,我们仍然无法访问它们。为此,我们必须执行以下操作之一:
要么像这样向对象添加泛型类型(我不想引发关于 super 或 extends 的争论,这只是一个例子):
public class Object<T extends Class> {
public final native T getClass();
}
Run Code Online (Sandbox Code Playgroud)
这是必需的,因为我们希望将某些类连接到某些对象。然而,我们并没有指定如何实例化该类。通常,类是通过类加载器实例化的。再次来自JavaDoc:
类没有公共构造函数。相反,Class 对象是由 Java 虚拟机在加载类时通过调用类加载器中的 DefineClass 方法自动构造的。
这将不再可行,除非我们需要某些方面,例如私有构造函数。此外,我们必须以本机 C++ 代码的形式提供每个自定义类。
另一种可能性是,在类似这样的实现中定义 getClass 方法(此代码具有不同的基于泛型的问题,但同样,只是一个示例):
public class Object {
public <T extends Class<?>> T getClass() {...}
}
public class MyType {
public MyClass getClass() { return new MyClass(); }
}
Run Code Online (Sandbox Code Playgroud)
这将为之前介绍的更复杂的问题打开大门。你现在可以在这里做你不应该做的工作。例如,想象一个IncallingHandler 。另一个问题是,现在开发人员可以自由地决定一个类是仅实例化一次还是多次,这会破坏基于类的编程,当然还有 Java 语言的某些方面。
尽管我无法肯定地回答,但我希望这对您有所帮助。