java.lang.Class
定义了多个方法,这些方法仅适用于某种类型的类,例如:
getComponentType
:仅与数组相关getEnumConstants
:仅与枚举有关例如,为什么没有ArrayClass
定义getComponentType
方法呢?
答案 0 :(得分:2)
这似乎或多或少是一种设计选择。由于我没有设计该语言,因此无法确定答案,但我将尽力解释造成这种情况的潜在原因。
那么为什么例如没有ArrayClass定义getComponentType方法呢?
了解这一点的诀窍是java.lang.Class
类与您正在编码的类并不直接等效。该特定类仅用于在运行时表示创建的类(例如:用于反射)。
从Java-Doc of the Class(此处为Java 7)开始:
类的实例表示正在运行的Java应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组还属于一个反映为Class对象的类,该对象由具有相同元素类型和维数的所有数组共享。 Java的原始类型(布尔,字节,字符,short,int,long,float和double)以及关键字void也表示为Class对象。
如果您正在编写代码,则您正在编写自定义类,这意味着您创建的代码就是该类。如果创建一个名为 ArrayClass 的类,则为您的类。然后java.lang.Class
类只能在运行时用于分析此特定Object的类。
对于Java设计,又增加了两层复杂性。
首先:您可以分析每个对象,它是java.lang.Object的子类型。方法getClass
在这里定义。这意味着您可以对任何对象进行内部检查以了解任何特定的细节。例如,如果Object的特定类型不是枚举,则该类在调用getEnumConstants
时返回null。如果您有java.lang.Class
的特定子类型,则必须先对实例进行内部检查,然后再进行内省,这会使它的使用更加烦人。
第二个:lazy在ClassLoader内创建了代表您类型的java.lang.Class
对象。在引用特定类型后,ClassLoader会检查是否已经加载了Class,如果尚未加载,则将其加载。然后,它创建java.lang.Class-Objects,它表示此特定的Type。然后可以使用该类对象来创建您类型的实例。
如果Java语言对于java.lang.Class具有不同的子类型,则ClassLoader将必须实例化Class的正确子类型,这与需要的类型相对应。如果您能够在其中创建java.lang.Class的自定义子类型,那么它将很快失去控制。 ClassLoader应该如何知道哪个Class-instance连接到您的Type?您是否必须编写特定的Class-instances并以某种方式标记您创建的类型以使用该Class-instance?您可以想象这样的事情:
public class MyType extends ... implements ... type MyClassInstance
但是,如何在自定义java.lang.Class实例中填充自定义字段?这些复杂性可能是java.lang.Class具有泛型类型(表示连接的Type)的原因。尽管有上百万种解决方案可以解决这些复杂问题,但对于可用性和鲁棒性仍有争议。
Oracle在Design Goals of the Java Programming Language中指出:
为满足这些需求而出现的系统非常简单,因此大多数开发人员都可以轻松对其进行编程;熟悉,以便当前的开发人员可以轻松地学习Java编程语言; ...
尽可能多地希望在语言中看到这样的东西,此功能会使它变得更加复杂,因为它可以使开发人员更改newInstance
方法的行为。您可以在此Method中引入一个Exception,并且可能会认为,构造函数抛出了Exception,即使它没有。这样做的方向与Why doesn't Java offer operator overloading?相同(或相似),因此,您实际上将覆盖new
关键字。
该类当前是最终的(可能是因为它是一种核心语言构造)。这禁止了java.lang.Class的子类型。如果该类应接收子类型,则必须放宽final的含义,这意味着任何人都可以覆盖该类的任何特定细节。调用getClass
可能会导致任何未知类型,这可能会做任何事情。
这样,我们现在只有类的特定子类型,我们仍然无法访问它们。为此,我们必须执行以下一项操作:
可以像这样向对象添加一个通用类型(我不想就super或extends展开辩论,这只是一个例子):
public class Object<T extends Class> {
public final native T getClass();
}
这是必需的,因为我们希望将某些类连接到某些对象。但是,我们没有指定如何实例化该类。通常,通过ClassLoader实例化一个类。再次来自JavaDoc:
类没有公共构造函数。取而代之的是,Java虚拟机会在加载类时以及通过调用类加载器中的defineClass方法自动构造Class对象。
这不再可行,除非我们需要某些方面,例如私有构造函数。另外,我们必须提供每个自定义类作为本地C ++代码。
另一种可能性是,在类似这样的实现中定义getClass方法(此代码具有不同的基于通用的问题,但仅是示例):
public class Object {
public <T extends Class<?>> T getClass() {...}
}
public class MyType {
public MyClass getClass() { return new MyClass(); }
}
这将为前面介绍的更复杂的问题打开大门。您现在可以在这里做您不应该做的工作。想象一个InvocationHandler。与此相关的另一个问题是,现在开发人员可以自由决定是否仅将Class实例化一次或多次,这可能会破坏Class-Based-Programming以及Java语言的某些方面。
即使我不能确定地回答,我希望这能有所帮助。