Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are there no subclasses of java.lang.Class?

Tags:

java.lang.Class defines multiple methods which only apply to a certain type of class, e.g.:

  • getComponentType: Only relevant for arrays
  • getEnumConstants: Only relevant for enums

So why is there, for example, no ArrayClass which defines the getComponentType method instead?

like image 844
Marcono1234 Avatar asked Dec 22 '18 01:12

Marcono1234


People also ask

What is the point of subclasses in Java?

A subclass is a class that extends another class. This works because a subclass inherits the functionality of the class it extends.

Are all Java classes subclasses of object?

All predefined classes and user-defined classes are the subclasses from Object class.

What is Lang class in Java?

Provides classes that are fundamental to the design of the Java programming language. The most important classes are Object , which is the root of the class hierarchy, and Class , instances of which represent classes at run time.


1 Answers

This appears to be more or less a design choice. Since i did not design this language, i cannot answer it with certainty, but i'll try to explain potential reasons for this.

So why is there for example no ArrayClass which defines the getComponentType method instead?

The trick to understanding this is, that the java.lang.Class class is not a direct equivalent to the class you are coding. This specific class is only used to represent the created class at runtime (for example: for the use of reflections).

As from the Java-Doc of the Class (here from Java 7):

Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.

You are writing custom classes, if you write your code, which means that your created code is the class. If you create a class called the ArrayClass, this is your class. The java.lang.Class class is then only usable at runtime to analyze the class of this specific Object.

For the design in Java, two more layers of complication are added.

First: You can analyze every object, which is a subtype of java.lang.Object. The method getClass is defined here. This means that you are able to introspect any object for any specific detail. If the specific type of the Object is not an enum for example, the class returns null upon calling getEnumConstants. If you had specific sub-types of the java.lang.Class, you would have to cast the instance before introspecting it, which would make it more annoying to work with.

Second: The java.lang.Class object, representing your type is created lazy, within the ClassLoader. Upon referenzing a specific type, the ClassLoader checks whether or not the Class is already loaded and if not, loads it. It then creates the java.lang.Class-Objects, which represents this specific Type. This class-object can then be used, to create instances of your type.

If the java language had different subtypes for the java.lang.Class, the ClassLoader would have to instantiate the correct subtype of the Class, corresponding to what would be needed. If you where able to create custom subtypes of the java.lang.Class, this would get out of hand quickly. How is the ClassLoader supposed to know which Class-instance is connected to your Type? Would you have to write specific Class-instances and somehow mark your created type to use that Class-instance? You could imagine something like this:

public class MyType extends ... implements ... type MyClassInstance 

But then, how do you fill up custom fields within your custom java.lang.Class instance? Those complications may be the reason, that the java.lang.Class has a generic type, representing the connected Type. Event though there are a million possible solutions to those complications, there still might be an argument to be made for usability and robustness.

As Oracle point out in there Design Goals of the Java Programming Language:

The system that emerged to meet these needs is simple, so it can be easily programmed by most developers; familiar, so that current developers can easily learn the Java programming language; ...

As much as one would love to see something like this within the language, this feature would make it way more complicated, since it would allow developers to change the behavior of the newInstance method for example. You could introduce an Exception within this Method and one might think, the constructor threw an Exception, even though it did not. This goes into the same (or a similar) direction as Why doesn't Java offer operator overloading?, with that, you are essentially overriding the new keyword.

The Class currently is final (likely because it is a core language construct). This prohibits subtypes of java.lang.Class. If the class should receive subtypes, it has to loose final which means anyone could override any specific detail for a class. Calling getClass could result in any unknown type, that could do anything.

With that, we now only have specific subtypes of classes, we still cannot access them. To do this, we have to do one of the following:

Either add a generic type to the Object like this (i don't want to start a debate about super or extends, this is just an example):

public class Object<T extends Class> {     public final native T getClass(); } 

This is required because we want to have certain classes connected to certain objects. With this however, we are not specifying how to instantiate the class. Normally a class is instantiated through the ClassLoader. Again from the JavaDoc:

Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader.

This would no longer be viable, except we require certain aspects like a private constructor. Also, we had to provide every custom Class as native C++ code.

The other possibility would be, to define the getClass method in implementations something like this (this code has different generic based issues, but again, just an example):

public class Object {     public <T extends Class<?>> T getClass() {...} }  public class MyType {     public MyClass getClass() { return new MyClass(); } } 

Which would open the door to more complicated issues introduced earlier. You could now do work in here, that you are not supposed to do. Imagine a InvocationHandler for example. The other issue with this is that now the developer has the freedom to decide whether or not a Class is instantiated only once or multiple times, which would break for example the Class-Based-Programming and of course some aspects of the Java-Language.

Even though i cannot answer with certainty, i hope this helped a bit.

like image 85
Thorben Kuck Avatar answered Oct 13 '22 06:10

Thorben Kuck