7

When reading this question, I open my editor to try some code samples to verify and understand it. The following is my code:

public enum EnumImpl {

    B {
        public void method() {
            System.out.println(s); //(1)non-static variable s cannot be referenced from a static context
        }
        public static int b;  //(2)Illegal static declaration in inner class
    };

    private int s;
}

But compiling the upper code makes me more confused.

  • The first error comes from what upper question shows that B actually belong to a static class. So in method, it is a static context.
  • The second error, by contrast, says that here is a inner class -- non-static nested class as java doc says.
  • The following is a line I cited from JLS, but it seems a little bit confusing and vague.

    A nested enum type is implicitly static.

  • The following is the byte code of anonymous synthetic class of B:

    final class enum_type.EnumImpl$1 extends enum_type.EnumImpl {
      enum_type.EnumImpl$1(java.lang.String, int);
        Code:
          0: aload_0
          1: aload_1
          2: iload_2
          3: aconst_null
          4: invokespecial #1                  // Method enum_type/EnumImpl."<init>":(Ljava/lang/String;ILenum_type/EnumImpl$1;)V
          7: return
    
      public void method();
        Code:
          0: return
    }
    

So the class of B is static or not?

@Lew Bloch seems saying it is like the following (the behavior matches with above enum example, but if this is true, the answer of the linked question is wrong in some senses).

abstract class Cmp {    
    private int s;
    static {
        class Bclass extends Cmp {
            public void method() {
//                System.out.println(s);
            }
//            private static int b;
        }
    }
}
Community
  • 1
  • 1
Tony
  • 5,972
  • 2
  • 39
  • 58
  • 1
    Interesting question. I tried to find a JLS chapter for that, but failed (still looking). The error on variable `b` is obvious: An enum constant with a class body declares and instantiates an anonymous class (which is an inner class) that does not allow static members. – Seelenvirtuose Dec 24 '16 at 09:38
  • What makes this even more interesting: Doing the same with a vanilla class (instead of an enum) lets the `method` compile fine (the error on variable `b` persists, of course). – Seelenvirtuose Dec 24 '16 at 09:40

2 Answers2

4

Your declaration of method() is in the wrong place. You declare it in the constant body. But it doesn't override anything. It belongs in the enum body, not the instance body.

The instance subtype is declared in the static initializer for the enum constant. Since the context is static it does not have access to the enum instance variables.

Your enum declaration is not static, it is top-level, and top-level classes cannot be static.

Constant bodies define an implicit nested anonymous subclass of the enum, and do not constitute nested enums as meant by the JLS. Each constant is of a different anonymous subtype of the enum you're declaring, which subtype is not static. However, the subtype is declared in a static context, so that's why the code can't reach the instance variable.

EDIT: Useful references from the JLS

https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.1 "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. Instance methods declared in these class bodies may be invoked outside the enclosing enum type only if they override accessible methods in the enclosing enum type (§8.4.8)."

https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.9.5 "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)."

Lew Bloch
  • 3,364
  • 1
  • 16
  • 10
  • First, I try make it override some method: make no difference. Second, you say 'It belongs in the enum body, not the instance body', please show your reference. Third, you answer seems contradict with the question I linked, who should I believe. Fourth, if It belongs in the enum body, why can't it access private variable? – Tony Dec 24 '16 at 08:14
  • 2
    The question is basically asking whether the enum constant `B` is static or not. OP gets two contradicting error messages regarding this topic. Your answer unfortunately does not address this question. – Seelenvirtuose Dec 24 '16 at 09:35
  • The question wasn't whether `B` is static, it was whether "B actually belong[s] to a static class", or "So the class of B is static or not?" The only enum type in the example was `EnumImpl`, and it is not static, nor nested. The only part of the question my answer addressed was that confusion. The other part I partially answered by pointing out that the `b` declaration was in an inner class. Why the static context? It has to do with `method` being in the wrong place, but you're right, I didn't explain that in detail. – Lew Bloch Dec 30 '16 at 06:15
  • Try moving `method` below the declaration of `s` and see what happens. – Lew Bloch Dec 30 '16 at 06:17
  • No, I disagree with you. If you view the byte code, you will see `B`'s class is not `EnumImpl`. It is a nested class extends `EnumImpl`. There are two enum types and one is nested, which I am asking. – Tony Dec 31 '16 at 02:37
  • The JLS reference talks about declarations. The special machinery of enum declarations gives each non-trivial constant an anonymous subclass, yes, but that's not going to be implicitly static because it's the special machinery doing it. From your point of view it's of the parent type. That nested class is defined in the static context of the constant declaration, so it's an anonymous class in a static context. The anonymous class is not static, but the context is. You can't access method as declared, so move it. Have you tried that, as suggested, or not? – Lew Bloch Dec 31 '16 at 22:45
  • See my edit, do you mean that? If that is true, then answer of my linked question is wrong. Any references? – Tony Jan 01 '17 at 11:34
  • 1
    The answer in the linked question is wrong, as I read https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.1. "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. Instance methods declared in these class bodies may be invoked outside the enclosing enum type only if they override accessible methods in the enclosing enum type (§8.4.8)." – Lew Bloch Jan 02 '17 at 21:20
  • 1
    https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.9.5 "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)." – Lew Bloch Jan 02 '17 at 21:22
  • Sorry didn't see you update comment for such a long time, thanks for your ref. You may update those refs in your answers :) – Tony Feb 11 '17 at 03:59
1

The error message "non-static variable s cannot be referenced from a static context" is puzzling because the issue isn't that s is static but that it isn't accessible in method. If you remove private from private int s;, then the error message goes away.

For the method declaration to be useful, one also needs to add public void method() {} or public abstract void method(); outside the declaration of B.

If you do both, the code

enum EnumImpl {

    B {
        public void method() {
            System.out.println(s); 
        }
    };

    public abstract void method();

    int s;
}

will compile, and you can call EnumImpl.B.method().

cayhorstmann
  • 3,192
  • 1
  • 25
  • 17