为什么`Class`课程最终?

时间:2014-07-28 12:46:14

标签: java security reflection decorator

在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;

}

但它缺乏透明装饰的美感。

2 个答案:

答案 0 :(得分:5)

根据 Class 类中的注释仅Java虚拟机创建Class      对象

如果允许扩展类,那么会出现问题,是否应该允许您创建自定义类对象?。如果是,那么它将破坏上述规则,即只有JVM才能创建类对象。所以,你没有给予这种灵活性。

PS:getClass()只返回已创建的类对象。它不会返回新的类对象。

答案 1 :(得分:0)

这是一个很好的问题,但这只是一个更普遍问题的一个实例,尽管四年前@TheLostMind刚刚就JVM规则/限制做出了回应(从实现角度考虑),但问题仍然存在:为什么选择JVM制定规则?必须有一个合理的理由。我们必须从更抽象的角度(角度)对其进行检查

简短答案:
可能所有与类型安全有关的事情,众所周知, Java是一种强类型语言,并且不允许任何人更改这一事实。

精心设计的答案(具有一定的上下文,以便每个人都可以理解): 所有故事都始于static and dynamic bindingsubtype 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#)允许选择静态方法分配还是动态方法分配。尽管这样做是出于效率的考虑,但事实表明,当在程序中同时使用两种调度机制时,这可能会使 该程序的含义。