在这里回答一个问题时,我想出了一个解决方案,如果可以扩展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;
}但它缺乏透明装饰的美。
发布于 2014-07-28 12:52:08
根据类中的注释,只有Java创建类对象.。
如果允许扩展类,则会出现问题,是否应该允许您创建自定义类对象?如果是,那么它将打破上述规则,即只有JVM才应该创建Class对象。所以,你没有那种灵活性。
PS:getClass()只返回已经创建的类对象。它不返回新的类对象。
发布于 2018-09-06 21:12:20
这是一个非常好的问题,但只是一个更普遍的问题的一个实例,尽管4年前@TheLostMind刚刚就JVM规则/限制做出了回应(被认为是一个实现的观点),但问题仍然存在:为什么JVM会提出这些规则?一定有一个合理的理由。我们必须从一个更抽象的层次(观点)来审视它。
简短答覆:
显然,这与类型安全有关,而且我们所知道的Java是一种强类型语言,不允许任何人改变这一事实。
详细回答(为了让每个人都能理解):所有的故事都是从静态和动态绑定开始的。亚型多态性提供的灵活性使得声明的(静态)对象类型与其运行时(动态)类型通常不同。运行时类型通常是静态类型的子类型.e,g.
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(车辆识别号码)相等,则认为它们是相等的。
因此,方法等于在类车辆中被相应地重新定义。继承方法的这种重新定义称为重写。注意,根据函数子类型规则,继承的方法参数的签名必须在子类中保持不变。这造成了一个尴尬的情况,因为在类Vehicle中,我们想要引用参数的VIN字段,而Object没有这样的字段。这就是为什么类型cast ( Vehicle )x指定的意图是将x视为车辆。无法静态地验证此转换,因此编译器将生成动态检查。这是动态类型检查的一个实例
为了正确地覆盖工作,要调用的方法由接收对象的动态类型(也称为动态调度(选择)方法 )决定,是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中重写的方法,因为是车辆的运行时类型。
在有些情况下,重写方法可能是有问题的,不应该允许。一个很好的例子是Java.lang.Object的getClass()。该方法在底层虚拟平台中有一个特定的实现,它保证了对该方法的调用确实将返回该方法的接收方的类对象。允许重写将对该方法的预期语义产生严重影响,从而在动态类型检查中产生一些重要问题。这可能就是为什么将getClass()声明为final的原因。
public class Object {
public final Class getClass();
....
}最后,Java中的类是最终类,即不能扩展,因此它的任何方法都不能被覆盖。由于类只有内省方法,这保证了类型系统在运行时的安全性,即在运行时不能更改类型信息。
把这个概念再扩展一点..。
基于接收对象类型的动态调度(选择)方法是面向对象语言的基本技术。它带来了使整个面向对象范式工作的灵活性类型。
通过继承向已经编译和运行的应用程序添加新类型只需要编译和链接新引入的类型,而不需要重新编译现有的应用程序。但是,这种灵活性在效率上带来了一定的损失,因为有关方法选择的决定被推迟到运行时。现代语言有高效的方法动态分配技术,但是一些语言如C++和C#通过提供静态绑定(方法选择)选项来避免相关的开销。在C#中,方法是静态绑定的,除非它们被显式声明为虚拟。例如:
public class Object {
public virtual boolean equals(Object x);
// other methods
}在C#中重写此方法将由显式关键字覆盖指示。例如:
public class Vehicle {
private int VIN;
private String make;
public override boolean equals(Object x) {
return (VIN == (Vehicle)x.VIN);
}
// other methods
}其接收方为类对象的方法总是静态绑定的。原因是该类的所有对象只有一个类对象。由于接收方是在编译时已知的,因此不需要将方法选择推迟到运行时。因此,这些方法被声明为静态方法,以指示它们属于类本身。一个例子是类车辆的numberOfVehicles方法,车辆数量不是单个车辆对象的属性。它是类载体的所有对象的属性,因此它属于类本身。例如:
public class Vehicle {
// fields;
public static int numberOfVehicles();
// other methods
}我们可以将上述所有讨论归纳如下:
-使用面向对象语言选择执行消息(方法分派)的方法的基本机制是动态的。它基于接收对象的运行时类型。
-静态(即类)方法的接收方是类对象。由于只有一个给定类型的类对象,所以静态方法的选择是静态的。
-某些语言(C++和C#)允许静态和动态方法分派的选择。虽然这样做是出于效率的原因,但已经表明,当在程序中使用这两种调度机制时,这可能会模糊程序的含义。
https://stackoverflow.com/questions/24995566
复制相似问题