5

Answering a question here at SO, I came up to a solution which would be nice if it would be possible extend the Class class:

This solution consisted on trying to decorate the Class class in order to allow only certain values to be contained, in this case, classes extending a concrete 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 ...
}

I know this code does not work as Class is final and I wonder why Class is final. I understand it must have to do with security but I can't imagine a situation where extending Class is dangerous. Can you give some examples?

By the way, the closer solution for the desired behavior I can reach is:

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;

}

But it lacks the beauty of transparent decoration.

  • unchangable maybe, :/ – Afzaal Ahmad Zeeshan Jul 28 '14 at 12:48
  • Although I think the accepted answer is absolutely correct, I might also mention that final classes can be optimized better by the JVM, so many of the very common classes are final. I think that's a part of the reasoning behind String being final (as well as the fact that mutable strings would also allow much more foot shooting and Java is all about saving feet) – Bill K Sep 06 '18 at 22:15

2 Answers2

5

According to comments in the Class class Only the Java Virtual Machine creates Class objects..

If you were allowed to extend classes, then a problem would arise, should you be allowed to create custom Class objects?. If yes, then it would break the above rule that only the JVM should create Class Objects. So, you are not given that flexibility.

PS : getClass() just returns the already created class object. It doesn't return a new class object.

TheLostMind
  • 35,966
  • 12
  • 68
  • 104
  • Sounds logic but if you take this rule literally, the rule would be still true if you are able to create objects of a class extending `Class`. – Pablo Francisco Pérez Hidalgo Jul 28 '14 at 13:07
  • @PabloFranciscoPérezHidalgo - Exactly.. And what would you do with the *custom* classes?. You would try to instantiate them right? (removing the *final* keyword would allow you to do so..). Imagine what would happen in that case.. The same applies to *String* class(it is also *final*).. if we were allowed to extend such classes, then we would *definately* try to change how things work internally in the JVM. – TheLostMind Jul 28 '14 at 13:11
0

That's a very good question but is only an instance of a more general problem and although @TheLostMind 4 years ago has just responded (considered an implementation point of view) in terms of JVM rules/restrictions the problem still remain: why JVM pose that rules? There have to be a plausible reason. We have to examine it from a more abstract level(point of view)

Short answer:
Plausibly all that has to do with type safety and as all we know Java is a strongly typed language and permits no one to change that fact.

Elaborate Answer(with some context so to be understandable for everyone): All the story start from static and dynamic binding. The flexibility given by subtype polymorphism makes the declared (static) type of an object in general different from its run-time (dynamic) type. The run-time type is in general a subtype of the static type. e,g.

AClass a = new AClass();
AsubClass b = new AsubClass(); //  AsubClass is derived from AClass
a=b;

The static type of a is AClass and after the assignment a=b; its runtime type is AsubClass. This has implications on selection of the most appropriate method when executing a message.

Now consider the class Vehicle given below.

public class Vehicle {
  private int VIN; // Vehicle Identification Number
  private String make;

  public boolean equals(Object x) {
    return (VIN == (Vehicle)x.VIN);
  }
  // other methods
}

The method equals in the root class java.lang.Object is defined as the test on object identity. This is the only meaningful way of defining the equality of objects in general. That is, two objects are equal if they have the same identity.

In a specific class a more suitable meaning of equality may be more appropriate. In the above class two vehicles are considered equal if their VINs (vehicle identification numbers) are equal.

So the method equals is redefined accordingly in the class Vehicle. This redefinition of an inherited method is called overriding. Note that the signatures of the inherited method arguments are required to remain the same in the subclass according to the function subtyping rule. This creates an awkward situation because in the class Vehicle we would like to refer to the VIN field of the argument, and Object does not have such a field. This is why the type cast (Vehicle)x specifies that the intent is to view x as a Vehicle. There is no way to verify this cast statically, hence a dynamic check is generated by the compiler. This is an instance of dynamic type checking.

In order for overriding to work correctly the method to be invoked is determined by the dynamic type of the receiver object (also known as dynamic dispatch (selection) of methods and is the most important case of dynamic binding in OO languages.) e.g.

Object a = new Object();
Object b = new Object();
Vehicle aV = new Vehicle();
Vehicle bV = new Vehicle();
a=aV; 
b=bV;
. . . 
a.equals(b)
. . .

The method to be invoked in response to the message a.equals(b) will be the method equals overridden in the class Vehicle because the run time type of a is Vehicle.

There are situations in which overriding a method might be problematic and should not be allowed. A good example is the Java.lang.Object 's getClass() . This method has a particular implementation in the underlying virtual platform, which guarantees that invocation of this method will indeed return the class object of the receiver of the method. Allowing overriding would have serious implications on the intended semantics of this method creating nontrivial problems in dynamic type checking. This is probably why the getClass() is declared as final. e.g.

public class Object {
  public final Class getClass();
  ....
}

Finally The class Class in Java is final, i.e. cannot be extended, and hence none of its methods can be overridden. Since the class Class has only introspection methods, this guarantees safety of the type system at run-time, i.e., the type information cannot be mutated at run time.

to extend the concept a bit more ...

Dynamic dispatch (selection) of methods based on the type of the receiver object is the basic technique in object-oriented languages. It brings the type of flexibility that makes the whole object-oriented paradigm work.

Adding new types by inheritance to an already compiled and running application requires only compilation and linking of the newly introduced types without recompiling the existing application. However, this flexibility comes with some penalty in efficiency because the decision about method selection is postponed to runtime. Modern languages have efficient techniques for dynamic dispatch of methods, but some languages like C++ and C# try to avoid the associated cost by providing a static binding (method selection) option. In C#, methods are statically bound unless they are explicitly declared as virtual. e.g.

public class Object {
  public virtual boolean equals(Object x);
  // other methods
}

Overriding this method in C# will be indicated by an explicit keyword override. e.g.

public class Vehicle {
  private int VIN;
  private String make;

  public override boolean equals(Object x) {
    return (VIN == (Vehicle)x.VIN);
  }
  // other methods
}

Methods whose receiver is the class object are always bound statically. The reason is that there is only one class object for all objects of that class. Since the receiver is known at compile time, there is no need to postpone method selection to run time. These methods are thus declared as static to indicate that they belong to the class itself. An example is the method numberOfVehicles of the class Vehicle The number of vehicles is not the property of individual vehicle objects. It is the property of all objects of the class Vehicle, hence it belongs to the class itself. e.g.

public class Vehicle {
  // fields;
  public static int numberOfVehicles();
  // other methods
}

We can summarize all the above discussion as follows:

– The basic mechanism for selecting a method for executing a message (method dispatch) in object-oriented languages is dynamic. It is based on the run-time type of the receiver object.

– The receiver of a static (i.e. class) method is the class object. Since there is only one class object of a given type, selection of a static method is static.

– Some languages (C++ and C#) allow a choice of static versus dynamic method dispatch. Although this is done for the reasons of efficiency, it has been shown that when both dispatch mechanisms are used in a program, that may obscure the meaning of the program.

Harry Kar
  • 11
  • 1
  • 4