0

According to the Java 8 Language Spec §15.8.2 (quote):

[...]

A class literal evaluates to the Class object for the named type (or for void) as defined by the defining class loader (§12.2) of the class of the current instance.

[...]

Mainly, 'the Class object' insinuates that this is or should be a singleton. Also §12.2 says:

[...]

Well-behaved class loaders maintain these properties:

  • Given the same name, a good class loader should always return the same class object.

[...]

In fact, using Java 8*, the following** prints true and true:

class Main {
    public static void main(String[] args) {
        Main main1 = new Main();
        Main main2 = new Main();
        System.out.println(main1.getClass().equals(main2.getClass()));
        System.out.println(main1.getClass() == main2.getClass());
    }
}

Is the class loader always 'well-behaved' and why (not)? In other words: are Class instances singleton? The other way around: can a Class of the same type be a different instance?

Notes: I do not refer to the singleton pattern here. However, if the Class implementation follows that pattern, that would be interesting. As a side-step, but by no means the main question: because the singleton pattern's legitimate uses are point of debate, would Java's Class be a good candidate to apply the singleton pattern to?

*:

$ java -version
openjdk version "1.8.0_262"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_262-b10)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.262-b10, mixed mode)

**: my IDE even warns me that the printed expressions are always true.

Erik
  • 4,305
  • 3
  • 36
  • 54

2 Answers2

3

You are using the term singleton incorrectly.

A singleton implies the existence of only one object of its own class. A Class object is an instance of the class java.lang.Class and there is more than one instance of this class. It’s actually impossible to have only one Class object, as the existence of a Class object does already imply the existence of at least two classes, java.lang.Object and java.lang.Class, so there must be at least two Class objects in the runtime.

Your example is not capable of spotting whether the class loader is well behaved or not. You already mentioned JLS §12.2

Well-behaved class loaders maintain these properties:

  • Given the same name, a good class loader should always return the same class object.

  • If a class loader L1 delegates loading of a class C to another loader L2, then for any type T that occurs as the direct superclass or a direct superinterface of C, or as the type of a field in C, or as the type of a formal parameter of a method or constructor in C, or as a return type of a method in C, L1 and L2 should return the same Class object.

A malicious class loader could violate these properties. However, it could not undermine the security of the type system, because the Java Virtual Machine guards against this.

Mind the last sentence. A JVM will guard against violations of these requirements. With the repeated occurrences of the same symbolic references within your example code, there are two possibilities

  1. The JVM remembers the result of the first resolution of this symbolic reference within this context and just reuses it on the next occurrence without asking a class loader again.

  2. The JVM remembers the result of the first resolution of this symbolic reference within this context and compares it with the result of subsequent resolutions of the same reference and throws an error if they mismatch.

Since both approaches imply remembering the result, the first approach is usually used when it comes to resolving the same reference within the same context, as it is simpler and more efficient. When it comes to different symbolic references that resolve to classes using the same reference, the JVM will indeed throw an error when the class loader constraints are violated.

So expressions like Main.class == Main.class or new Main().getClass() == new Main().getClass() will never evaluate to false. Most likely, the resolution of the symbolic reference to Main will go a short-cut, using the same runtime class regardless of what the class loader would do. But even when it does not take the short-cut and the ClassLoader is misbehaving, returning a different class object for the next query, the JVM would detect it and throw an error, so the expression would not evaluate to a boolean result at all. In neither case could it result in false.

Holger
  • 285,553
  • 42
  • 434
  • 765
0

In a single classloader, the Class object is the same.

Is the class loader always 'well-behaved' and why (not)?

It really depends on the implementation. If it is done deliberately that always the classloader always returns a new Class-Object, it won't be well-behaved. At least all classloaders of OpenJDK are well-behaving.

In other words: are Class instances singleton? The other way around: can a Class of the same type be a different instance?

In one single classloader, every Class instance is a singleton. With multiple classloaders, following will evaluate to false:

ClassLoaderA.getInstance().loadClass("foo.Bar")==ClassLoaderB.getInstance().loadClass("foo.Bar");

The other way around: can a Class of the same type be a different instance?

Only if loaded by two different, conforming,well-behaved classloaders.

As a side-step, but by no means the main question: because the singleton pattern's legitimate uses are point of debate, would Java's Class be a good candidate to apply the singleton pattern to?

This is quite opinion-based, but I think, it's no good candidate to apply the singleton pattern, as most singletons are implemented like this:

class Foo{
   public static final Foo INSTANCE=new Foo();
   private Foo(){
      ìf(INSTANCE!=null)
         throw new IllegalAccessException("No Foo instances for you!");
   }
}

So more that it is really ONE object of a Class, many that only differ by some small things like a different Classloader.

JCWasmx86
  • 3,473
  • 2
  • 11
  • 29
  • *"If it is a bad custom implementation, that always returns a new Class-Object, it won't be well-behaved."* - You are hypothesizing a JVM bug here. It is possible in theory, but in practice ... it will only happen if someone did it deliberately. – Stephen C Aug 10 '20 at 08:34
  • @StephenC I meant it like that, I just misphrased it. Thanks for pointing it out – JCWasmx86 Aug 10 '20 at 08:36