24

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

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

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Marcono1234
  • 5,856
  • 1
  • 25
  • 43
  • 3
    Good question. My intuition would be that `Class` is a proxy of a low-level data structure which doesn't leverage any sort of inheritance. – robinsax Dec 22 '18 at 01:39
  • 2
    I also think that the reason might be that it's rather low-level, elementary, and needs a rather "direct" representation in the JVM. Another reason is that public/protected constructors and possible subclassing might have some security implications that could be hard to deal with. – Marco13 Dec 22 '18 at 01:57
  • 8
    Are you looking for a *philosophical* reason? It's hard to answer a **why** question like this with anything other than **because there aren't**. – Elliott Frisch Dec 22 '18 at 02:08
  • @ElliottFrisch Agreed, to some extent: If one could lay out, for example, a possible attack vector or security vulnerability that could be opened via subclassing, then this could be a profound technical reason. The argument of the representation in the native side is, to some extent, weakened by http://hg.openjdk.java.net/hsx/hsx25/hotspot/file/a5d6f0c3585f/src/share/vm/oops/klass.hpp vs. http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/oops/arrayKlass.hpp ... – Marco13 Dec 22 '18 at 14:22
  • @ElliottFrisch, I hoped that there was a reason for this design decision, such as the ones suggested in the other comments. – Marcono1234 Dec 22 '18 at 15:24
  • 1
    This is a question for James Gosling. – Peter Cetinski Dec 22 '18 at 15:37
  • @Marcono1234 what would these new sub-classes `ArrayClass`, `EnumClass`, `PrimitiveClass`, `InterfaceClass`, `AnnotationClass` give you? – Andrew Tobilko Dec 27 '18 at 21:39
  • @AndrewTobilko they would contain the class specific methods as listed above and would remove them from the common base class `Class` where they are in most cases irrelevant. – Marcono1234 Dec 27 '18 at 21:55
  • @Marcono1234 are you ok with methods such as `isEnum`, `isArray` in `Class`? – Andrew Tobilko Dec 28 '18 at 13:18
  • @AndrewTobilko, not really, this makes it even worse. When I get the class of an enum (e.g. `MyEnum.class`), then why do I need methods testing if the class belongs to an enum? – Marcono1234 Dec 28 '18 at 18:26
  • @Marcono1234 what would you like `getClass()` or `.class` to return? (Sorry for asking too many questions - I want to understand why you find the current implementation bulky) – Andrew Tobilko Dec 28 '18 at 19:56
  • @AndrewTobilko, `MyEnum.class` = `EnumClass`; `int[].class` = `ArrayClass`; with `EnumClass` defining `getEnumConstants` and `ArrayClass` `getComponentType` – Marcono1234 Dec 28 '18 at 21:15
  • @Marcono1234 how is it possible to return different things from the same single interface? – Andrew Tobilko Dec 29 '18 at 12:12

1 Answers1

3

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.

Thorben Kuck
  • 1,092
  • 12
  • 25
  • Thanks for your answer. I would expect that arrays `getClass()` returned for example `ArrayClass extends int[]>`, therefore there should not be any annoying casting required. Additionally I am not asking for custom meta-classes (with your `type` example), but only for subclasses for already built-in classes. – Marcono1234 Dec 27 '18 at 18:32
  • @Marcono1234 This would require the change of `Class> getClass()` to something like `> getClass()`. Therefor either the Object class requires a generic type to define the class or the Method has to be implemented to return a specific class instance. This would again negate the "analyze any object" aspect. The Class-class is a broad, generic representation of any and all classes – Thorben Kuck Dec 28 '18 at 12:25
  • `Sub>` is a subtype (hope that is the correct term) of `Super>`, you can probably find the reason for this somewhere in the [JLS](https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html#jls-18.2.3), but you can also try it out. Therefore it would be possible to have `public ArrayClass> getClass()`. Additionally `getClass` returns already `Class extends T>`, see [doc](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#getClass--). – Marcono1234 Dec 28 '18 at 19:47
  • @Marcono1234 Yes, that is true. Theoretically, you could have ArrayClass. To realize that however, class has to loose the final keyword, which means anyone can manipulate (for example) the new keyword. Also, as long as you have a concrete Type, your example works perfectly fine (i.e. `ArrayClass arrayClass = Array.class`). But as soon as you have an object of unspecified type, this gets more complicated. I don't say i agree with either more, i believe it is just a design choice. – Thorben Kuck Dec 29 '18 at 11:19
  • @Marcono1234 I edited the answer. Maybe it is more clear now – Thorben Kuck Dec 29 '18 at 12:05
  • @Marcono1234 Is this answer not suitable for your, or is my wording off and not understandable? – Thorben Kuck Jan 07 '19 at 12:01
  • I would say kind of not suitable. Your answer is partially based on creating custom meta classes which I was not asking for. I guess the problem that `getClass` is and has to be final is a good point, but I would like to leave this question unanswered for now. – Marcono1234 Jan 07 '19 at 18:47