在SO处回答了一个问题,我找到了一个解决方案,如果有可能扩展Class
类,那就很好了:
这个解决方案包括尝试装饰Class
类,以便只允许包含某些值,在本例中,是扩展具体类C
的类。
public class CextenderClass extends Class
{
public CextenderClass (Class c) throws Exception
{
if(!C.class.isAssignableFrom(c)) //Check whether is `C` sub-class
throw new Exception("The given class is not extending C");
value = c;
}
private Class value;
... Here, methods delegation ...
}
我知道此代码不起作用,因为Class
是最终的,我想知道为什么Class
是最终的。 我知道这必须与安全有关,但我无法想象延伸Class
是危险的。你能举一些例子吗?
顺便说一句,我能达到的理想行为的更接近的解决方案是:
public class CextenderClass
{
public CextenderClass(Class c) throws Exception
{
if(!C.class.isAssignableFrom(c)) //Check whether is `C` sub-class
throw new Exception("The given class is not extending C");
value = c;
}
public Class getValue() {
return value;
}
private Class value;
}
但它缺乏透明装饰的美感。
答案 0 :(得分:5)
根据 Class 类中的注释仅Java虚拟机创建Class 对象强>
如果允许扩展类,那么会出现问题,是否应该允许您创建自定义类对象?。如果是,那么它将破坏上述规则,即只有JVM才能创建类对象。所以,你没有给予这种灵活性。
PS:getClass()
只返回已创建的类对象。它不会返回新的类对象。
答案 1 :(得分:0)
这是一个很好的问题,但这只是一个更普遍问题的一个实例,尽管四年前@TheLostMind刚刚就JVM规则/限制做出了回应(从实现角度考虑),但问题仍然存在:为什么选择JVM制定规则?必须有一个合理的理由。我们必须从更抽象的角度(角度)对其进行检查
简短答案:
可能所有与类型安全有关的事情,众所周知, Java是一种强类型语言,并且不允许任何人更改这一事实。
精心设计的答案(具有一定的上下文,以便每个人都可以理解): 所有故事都始于static and dynamic binding。 subtype polymorphism提供的灵活性使对象的声明(静态)类型通常不同于其运行时(动态)类型。 运行时类型通常是静态类型的子类型。 例如,
AClass a = new AClass();
AsubClass b = new AsubClass(); // AsubClass is derived from AClass
a=b;
a的静态类型是AClass,并且在赋值a = b之后; 其运行时类型是AsubClass。这对执行消息时选择最合适的方法有影响。
现在考虑给定的车辆类别 下面。
public class Vehicle {
private int VIN; // Vehicle Identification Number
private String make;
public boolean equals(Object x) {
return (VIN == (Vehicle)x.VIN);
}
// other methods
}
在根类java.lang.Object中等于的方法定义为对对象标识的测试。 通常,这是定义对象相等性的唯一有意义的方法。 也就是说,如果两个对象具有相同的标识,则它们相等。
在特定类别中,更合适的相等含义可能更合适。在以上类别中,如果两个车辆的VIN(车辆识别号)相等,则认为它们相等。
因此equals方法在 类车辆。这是对继承的重新定义 该方法称为覆盖。 请注意,根据function subtyping rule,要求继承的方法参数的签名在子类中保持相同。 这会造成尴尬的情况,因为在Vehicle类中,我们想引用参数的VIN字段,而Object没有这样的字段。这就是为什么类型(Vehicle)x类型指定将x视为车辆的意图。没有办法 静态验证此转换,因此编译器会生成动态检查。这是dynamic type checking.
的一个实例为了使覆盖正常工作,要调用的方法由接收方对象的动态类型(也称为dynamic dispatch (selection) of methods)确定,这是OO语言中动态绑定的最重要情况。 例如
Object a = new Object();
Object b = new Object();
Vehicle aV = new Vehicle();
Vehicle bV = new Vehicle();
a=aV;
b=bV;
. . .
a.equals(b)
. . .
要响应消息a.equals(b)调用的方法将是在Vehicle类中重写的equals方法,因为a的运行时类型为Vehicle。
在某些情况下,重写方法可能会出现问题,因此不应该被允许。一个很好的例子是Java.lang.Object的getClass()。该方法在基础虚拟平台中具有特定的实现,该方法可确保对该方法的调用确实会返回该方法的接收者的类对象。 允许覆盖将严重影响此方法的预期语义,从而在动态类型检查中产生不小的问题。这可能就是为什么将getClass()声明为final的原因。 例如
public class Object {
public final Class getClass();
....
}
最后,Java中的类Class是最终的,即无法扩展,因此不能重写其任何方法。 由于类Class仅具有自省方法,因此可以保证类型系统在运行时的安全性,即,类型信息不能在运行时进行更改。
进一步扩展概念...
基于接收者对象类型的方法的动态调度(选择)是面向对象语言中的基本技术。 它带来了使整个面向对象范例都能工作的灵活性。
通过继承将新类型添加到已编译并正在运行的应用程序中,仅需要编译和链接新引入的类型,而无需重新编译现有应用程序。但是,这种灵活性会带来效率方面的损失,因为有关方法选择的决定会推迟到运行时。现代 语言具有动态分配方法的有效技术,但是某些语言(例如C ++和C#)尝试通过提供静态绑定(方法选择)选项来避免相关的开销。在C#中,方法是静态绑定的,除非将它们明确声明为 virtual 。 例如
public class Object {
public virtual boolean equals(Object x);
// other methods
}
在C#中重写此方法将由显式关键字 override 指示。 例如
public class Vehicle {
private int VIN;
private String make;
public override boolean equals(Object x) {
return (VIN == (Vehicle)x.VIN);
}
// other methods
}
其接收者是类对象的方法总是静态绑定的。 原因是该类的所有对象只有一个类对象。由于接收器在编译时是已知的,因此无需将方法选择推迟到运行时。因此,这些方法被声明为静态的,以指示它们属于类本身。 一个示例是Vehicle类的numberOfVehicles方法。车辆数量不是单个车辆对象的属性。它是 Vehicle类的所有对象的属性,因此它属于类本身。 例如
public class Vehicle {
// fields;
public static int numberOfVehicles();
// other methods
}
我们可以将上述所有讨论总结如下:
–选择执行消息的方法的基本机制(方法 面向对象语言中的调度是动态的。它基于接收者对象的运行时类型。
–静态(即类)方法的接收者是类对象。由于只有 一个给定类型的类对象,选择静态方法是静态的。
–某些语言(C ++和C#)允许选择静态方法分配还是动态方法分配。尽管这样做是出于效率的考虑,但事实表明,当在程序中同时使用两种调度机制时,这可能会使 该程序的含义。