28

I just answered this question by saying how to solve the compilation problem:

How to use fields in java enum by overriding the method?

But what I don't understand is why the error is happening in the first place.

Here is the example written as an enum:

public enum MyEnum {


    FIRST {
        @Override
        public String doIt() {
            return "1: " + someField; //error
        }
    },
    SECOND {
        @Override
        public String doIt() {
            return "2: " + super.someField; //no error
        }
    };

    private String someField;


    public abstract String doIt();

} 

Here is the exact same thing as abstract classes

abstract class MyClass {
    class FIRST extends MyClass {
        @Override
        public String doIt() {
            return "1: " + someField; //no error
        }
    };
    class SECOND extends MyClass {
        @Override
        public String doIt() {
            return "2: " + super.someField; //no error
        }
    };

    private String someField;

    public abstract String doIt();
}

In the case of FIRST within the enum implementation it cannot access someField. However in the abstract class case it can.

Additionally adding super fixes the problem, as does removing the private modifier on the field.

Does anyone know why this slight quirk in the behaviour is happening?

Community
  • 1
  • 1
Tim B
  • 40,716
  • 16
  • 83
  • 128

4 Answers4

18

Your abstract class is not equivalent to your enum, since enums are implicitly public static final. Thus, you'll observe the same behavior if you use:

abstract class MyClass {

    static class FIRST extends MyClass {

        @Override
        public String doIt() {
            return "1: " + someField; // error
        }

    };

    static class SECOND extends MyClass {

        @Override
        public String doIt() {
            return "2: " + super.someField; // no error
        }

    };

    private String someField;

    public abstract String doIt();

}

As explained in http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html, chapter "Static Nested Classes":

A static nested class cannot refer directly to instance variables or methods defined in its enclosing class: it can use them only through an object reference.

Thus the need of super. You could also use this if the field were protected rather than private.

Natan Streppel
  • 5,759
  • 6
  • 35
  • 43
sp00m
  • 47,968
  • 31
  • 142
  • 252
  • Thanks @sp00m, I did have a quick look at the enum spec before I posted the question but missed the interaction with static inner classes and variable access. – Tim B Jul 29 '14 at 09:07
  • Enums are only implicitly final if none of their enum constants have their own class bodies, as stated in [JLS 8.9](http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9). Aside from the subclasses created for the enum constants, though, you still can't extend non-final enums. – user2357112 Jul 29 '14 at 13:32
4

When an identifier is resolved, Java prefers the lexical scope over inherited members. So when you have an inner class that extends the outer class and use a field of the outer class without using this or super, the field of the outer instance is accessed which fails if the inner class is static as there is no outer instance then. In contrast, when using super you are explicitly accessing the inherited member. Note that enum classes are implicitly static. You can even use this to access the inherited member but you have to use ((MyClass)this).someField to access it if it’s declared private.

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

Class FIRST is an inner class of MyClass and also a sub class. The reason you do not see an error when accessing someField in it is because you are accessing the someField of the outer class, not the super class.

class MyClass {
    class FIRST extends MyClass {
        @Override
        public String doIt() {
            super.someField = "super";
            return "1: " + someField;
        }
    };

    private String someField = "outer";

    public String doIt(){return "";}

    public static void main(String[] args) {
        System.out.println(new MyClass().new FIRST().doIt());
    }
}

Prints 1: outer.

In the other case your enum constants behave as static nested sub classes, not inner classes, so they do not have a reference to the outer class, only their super class.

Alex - GlassEditor.com
  • 14,957
  • 5
  • 49
  • 49
0

I disagree with the accepted answer.

The enum const declaration is implicit public static final, but not the class the enum const belongs to.

From JSL Chapter 8.Classes

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.

And what the 'rules of anonmous classes'?

From JSL Chapter 15:

An anonymous class declaration is automatically derived from a class instance creation expression by the Java compiler.

An anonymous class is never abstract (§8.1.1.1).

An anonymous class is always implicitly final (§8.1.1.2).

An anonymous class is always an inner class (§8.1.3); it is never static (§8.1.1, §8.5.1).

And if the enum equivalent class is a static class, how to explain the following error?

public enum MyClass {
    First {
        public static int b;  //(2)Illegal static declaration in inner class
    };
}

But why a inner class can't access the outer class's field?

A possible enum equivalent class may looks like following, which gives the same error as a enum class:

abstract class MyClass {    
    private int someField;
    static {
        class First extends MyClass {
            public void method() {
                System.out.println(someField);
            }
            private static int b;
        }
    }
}

More:

Community
  • 1
  • 1
Tony
  • 5,972
  • 2
  • 39
  • 58