5

Possible Duplicate:
Does Java guarantee that Object.getClass() == Object.getClass()?

I noticed that Eclipse generates this code for equals:

public class MyClass {

    public boolean equals(Object obj) {
        if (this == obj)
            return true;

        if (obj == null)
            return false;

        if (getClass() != obj.getClass())
            return false;

        MyClass other = (MyClass) obj;

        // ...
    }

}

Of particular interest is this code:

if (getClass() != obj.getClass())
    return false;

The code assumes that the Class object returned by getClass() will be the same instance (not just an equivalent instance) for all objects of the same class. That is, they didn't deem it necessary to write it like this:

if (getClass().equals(obj.getClass()))
    return false;

Does Java officially document this behavior of the getClass() method?

Community
  • 1
  • 1
Adam Paynter
  • 46,244
  • 33
  • 149
  • 164

7 Answers7

12

Yes, the class object will be the same so long as the two classes were loaded by the same classloader.

But if they weren't, the two classes have to be regarded as different, even though they may share the same name and code. (This is something that can easily be run into when using multiple classloaders, so it's worth remembering.)

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • 1
    So the general answer is: No. And people that work with OSGI know this classloading nightmare (**Damn** - why do I get an error? It compiles, it **is** the correct type, just look at the name!!!!) – Andreas Dolk Jan 24 '11 at 14:43
  • 3
    @Andreas_D No, the general answer is a definite yes. The same object will always have the same class instance returned. Two similarly looking objects might have different class instances returned, but that's because the two classes are actually distinct. – biziclop Jan 24 '11 at 14:48
  • Thanks for the answer! However, I'm not completely convinced of this argument. I agree that a class depends on both its *class name* and its *class loader* to establish its identity. That said, the language designers could have simply built that logic into the `equals` method of `Class` instead of forcing *the same instance* to be returned every time `getClass()` is called. For example, as long as the `Class`'s `equals` method tested `this.className.equals(other.className) && this.classLoader.equals(other.classLoader)`, we could return different instances, correct? – Adam Paynter Jan 25 '11 at 12:09
  • 1
    @Adam Paynter It's true, they could've done it that way. But how do you implement `ClassLoader.equals()` then? Classloaders have no natural (primitive) unique identifiers, so no matter how deeply you bury it, at some point you have to check for two instances of something being the same. Another catch is that calling `Class.getClassLoader()` might not be allowed by the security manager. – biziclop Jan 25 '11 at 13:12
  • @Adam Paynter Just a little curiosity, you can also check the equality of two classes with `class1.isAssignableFrom(class2) && class2.isAssignableFrom(class1)` – biziclop Jan 25 '11 at 13:15
3

Does Java officially document this behavior of the getClass() method?

Yes it does. I haven't (yet) found an explicit statement, but there are numerous places where this is implied ... or stronger ... by the javadocs. For example:

  1. There is no javadoc for Class.equals(Object) which means that Object.equals(Object) is not overridden.

  2. The Object.getClass() method is specified as returning "the runtime class of this Object". (Note it says "the" not "a".) The javadoc also says "[t]he returned Class object is the object that is locked by static synchronized methods of the represented class". If there was more than one such object for any class, then the specified mechanism for synchronizing would simply not work.

  3. The Class.forName(...) methods are specified as returning "the Class object associated with the class or interface with the given string name". (Note again, it says "the" not "a".)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
3

That really depends on the class loader and as i understand is not guaranted:

http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44459

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.

Péter Török
  • 114,404
  • 31
  • 268
  • 329
PeterMmm
  • 24,152
  • 13
  • 73
  • 111
  • It says a great deal about java that they've thought about what happens when you have a malicious classloader. – sblundy Jan 24 '11 at 14:41
  • If you have a malicious class loader it can give classes it create all permissions - game over. / The Java EE spec suggests using class loaders that prefer their own classes over those of their parents, completely against the Java SE spec. – Tom Hawtin - tackline Jan 24 '11 at 15:01
2

There's (at least) implicit documentation of this fact (quoted from the JavaDoc, emphasis mine):

Returns: The Class object that represents the runtime class of this object.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
1

I know of one instance in which it isn't, but by design. If the classes were loaded by different classloader, they won't be the same instance because to the JVM the classes are different.

sblundy
  • 60,628
  • 22
  • 121
  • 123
  • 1
    If they are loaded by different classloaders then it's not really the same class. – Joachim Sauer Jan 24 '11 at 14:32
  • @Joachim Sauer: True of course. But that's not what you're thinking when you're debugging the ClassCastException – sblundy Jan 24 '11 at 14:39
  • For reasons like this, I wish that Java paid more attention to the `Class`/`ClassLoader` relationship in its `ClassCastException`s. – Adam Paynter Jan 24 '11 at 15:01
  • Thanks for the answer! However, I don't know if I'm totally convinced (see my comment on [biziclop's answer](http://stackoverflow.com/questions/4783101/does-java-guarantee-that-the-class-object-returned-by-getclass-will-always-be-t/4783149#4783149)). – Adam Paynter Jan 25 '11 at 12:13
1

If both of the class instance are the same you can be sure that the objects are of the the same type.

Altough I have found no specific reference to answer your question, just for completeness the Java Language Specification book, section #4.3.4 says:

4.3.4 When Reference Types Are the Same

Two reference types are the same run-time type if:

  • They are both class or both interface types, are defined by the same class loader, and have the same binary name (§13.1), in which case they are sometimes said to be the same run-time class or the same run-time interface.

  • They are both array types, and their component types are the same run-time type(§10).

vz0
  • 32,345
  • 7
  • 44
  • 77
1

Here's one working example that demonstrates that classes with the same classname may have different Class instances. But equals shows that the class instances really are different, even if the classes share the same name and are loaded from the same resource.

For preparation: create a simple class in the default package:

public class SomeClass{}

store the classfile (only!!) in a folder that is not on the classpath (like /tmp).

Then execute this method:

public static void test() {
  try {
    URL[] urls = new URL[]{ new File("/tmp").toURL() };
    ClassLoader cl1 = URLClassLoader.newInstance(urls);
    ClassLoader cl2 = URLClassLoader.newInstance(urls);
    Class clazz1 = cl1.loadClass("SomeClass");
    Class clazz2 = cl2.loadClass("SomeClass");

    System.out.println("Same names?            " + clazz1.getName().equals(clazz2.getName()));
    System.out.println("clazz1 == clazz2:      " + (clazz1 == clazz2));
    System.out.println("clazz1.equals(clazz2): " + (clazz1.equals(clazz2)));
  } catch(Exception e) {
    e.printStackTrace();
  }
}
Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268