2

Consider the following Enum classes:

public enum APlanet {
    VENUS   ()  {public void stuff(){}},
    EARTH   () {public void stuff(){}},
    MARS    ()  {public void stuff(){}};
    public abstract void stuff();
}

public enum BPlanet {
    VENUS   (),
    EARTH   (),
    MARS    ();
}

Then APlanet.MARS.getClass().isEnum() returns false whereas BPlanet.MARS.getClass().isEnum() returns true. Why? Notice that APlanet.getDeclaringClass().isEnum() correctly returns true.

Specifically, I'm trying to reliably test if an Object is Enum:

Object a = APlanet.MARS;
Object b = BPlanet.MARS;
a.getClass().isEnum() /* returns false */
b.getClass().isEnum() /* returns true  */

yet

Enum.class.isAssignableFrom(a.getClass()); /* returns true */

It's a little confusing that the inner class APlanet.MARS is not an Enum yet you can assign it to an Enum, as in:

Enum<?> m = APlanet.MARS;
  • Thanks for the fast response. How can I reliably test if an Object is an instance of an Enum? – user2779878 Sep 14 '13 at 21:44
  • What do you mean by that? Can you add an example? If you are in need to doing something like that, then I guess there is some issue with your design. – Rohit Jain Sep 14 '13 at 21:54

1 Answers1

8

From JLS Section 8.9.1 - Enum Constants:

The optional class body of an enum constant implicitly defines an anonymous class declaration (§15.9.5) that extends the immediately enclosing enum type. The class body is governed by the usual rules of anonymous classes; in particular it cannot contain any constructors.

You have declared the constant-specific class body in your first enum:

 MARS ()  {public void stuff(){}}; // The curly braces defines a class body

This creates an anonymous class. So, MARS.getClass() will return the Class instance for this anonymous class, which is not an Enum.


This is similar to case where you create an instance of anonymous subclass of an interface, or any other class like this:

SomeInterface obj = new SomeInterface() { /** Method definitions **/ };

and then obj.getClass() wouldn't return SomeInterface but, ClassContainingThatDeclaration$1.

Notice that APlanet.getDeclaringClass().isEnum() correctly returns true

I suspect that should be - APlanet.MARS.getDeclaringClass().isEnum(). That would return true, as per the documentation of Enum#getDeclaringClass() method:

Returns the Class object corresponding to this enum constant's enum type. Two enum constants e1 and e2 are of the same enum type if and only if e1.getDeclaringClass() == e2.getDeclaringClass(). (The value returned by this method may differ from the one returned by the Object.getClass() method for enum constants with constant-specific class bodies.)


But I thought an enum can't be subclassed?

Now the doubt that can come to one's mind is, an enum can't be subclassed. So, how can the anonymous subclass of enum be created? How can it extend the enum? That's because, an enum is final unless it declares some constant-specific class body, as per JLS Section 8.9 - Enums:

An enum type is implicitly final unless it contains at least one enum constant that has a class body.

You still can't explicitly extend an enum:

Even though the constant-specific class body implicitly creates an anonymous sub-class, you can't explicitly extend an enum. As per JLS Section 8.1.4 - Superclasses and Subclasses:

It is a compile-time error if the ClassType names the class Enum or any invocation of it.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • +1 It's [this](http://stackoverflow.com/questions/13670991/interview-can-we-instantiate-abstract-class/13671003#13671003) all over again :). And congrats on getting "Legendary"! – Bohemian Sep 14 '13 at 20:07
  • @Bohemian. Ah! Thanks :) Quite happy about it. – Rohit Jain Sep 14 '13 at 20:10