30

The task is to implement beautiful strategy design pattern with the java enum:

public enum MyEnum {

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

    private String someField;

    public abstract String doIt();

} 

but when referring to someField I get

Cannot make a static reference to the non-static field someField.

What is wrong and is it possible to do that better?

Anubian Noob
  • 13,426
  • 6
  • 53
  • 75
Mark
  • 17,887
  • 13
  • 66
  • 93
  • try typing `this.someField` and see if it works, and also I think you need to initialize it with a constructor maybe. – EpicPandaForce Jul 29 '14 at 08:09
  • 3
    That's very odd - I'm surprised that the override counts as a static context. Note that making the field protected removes the error, which is also odd... – Jon Skeet Jul 29 '14 at 08:09
  • 1
    @Zhuinden: That changes the error to `someField has private access in MyEnum` – Jon Skeet Jul 29 '14 at 08:10
  • @JonSkeet Yes, it's odd. Normally classes in the same .java file can see each other's private fields but something slightly unusual is happening here. I tried both protected and just removing private and they both work. – Tim B Jul 29 '14 at 08:11
  • @JonSkeet well that gives vision to what the error actually is, I personally had not known that the enum would be a subclass of the original `MyEnum` class when it provides an implementation to an abstract function, although it does make sense. This is interesting. – EpicPandaForce Jul 29 '14 at 08:12
  • @TimB removing private makes it be `package` visibility which is apparently visible from subclasses too. – EpicPandaForce Jul 29 '14 at 08:15
  • @Zhuinden Sub classes can see private member variables though. – Tim B Jul 29 '14 at 08:25
  • 1
    I've asked the bigger question here: http://stackoverflow.com/questions/25011061/why-can-enum-implementations-not-access-private-fields-in-the-enum-class as this question is answered. – Tim B Jul 29 '14 at 08:26
  • @TimB I'm pretty sure when you `extend` from a base class, then `private` is visible only from the base class, while `protected` is what is visible in the base class and all its subclasses. But if this is different in case of enums or in case of anonymous subclasses, then I am probably wrong. – EpicPandaForce Jul 29 '14 at 08:26
  • 2
    So basically `FIRST` and `SECOND` can be regarded as static inner classes that override `MyEnum`? If you think that way it will explain why the compiler thinks `someField` as a static reference @JonSkeet is surprised about. And adding `this` will make it explicitly seek for the member in the base class, which changes the error to `someField has private access in MyEnum`. Am I right? P.S. And also it would explain why we can refer to enum values as `MyEnum.FIRST` in the code. – sampathsris Jul 29 '14 at 08:59
  • 1
    You can use `((MyEnum)this).someField` to access the `private` field as an inner class can access `private` fields only if the instance has the outer class type. But of course, `super.someField` is shorter… See [here](http://stackoverflow.com/a/25011218/2711488) – Holger Jul 29 '14 at 09:16
  • Note that `FIRST.someField` is valid, though it's obviously not the ideal solution. – shmosel Mar 18 '16 at 03:46

8 Answers8

29

A specialized enum is nothing but a subclass with inner-class semantics. If you look at the byte code after compilation, you will notice that the compiler only inserts accessor method for reading a private field but any specialized enum is compiled as its own class. You can think about your enum as being implemented as:

public abstract class MyEnum {

  private static class First extends MyEnum {

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

  private static class Second extends MyEnum {

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

  public static final MyEnum FIRST = new First();
  public static final MyEnum SECOND = new Second();

  private String someField;

  public abstract String doIt();
} 

As you can see, the same compiler errors occur. Effectively, your problem does not relate to enums but to their inner-class semantics.

However, you found a borderline case of the compiler guessing the intend of your code and trying to warn you that what you intend is illegal. In general, the someField field is visible to any specialized enum. However, there are two ways of accessing the private field from an inner class and only one is legal:

  1. private members are not inherited. You can therefore not access a private field from this instance when it was defined in a super class.

  2. For inner classes, members of outer classes are accessible even if they are private. This is achieved by the compiler by inserting accessor methods to the outer classes which expose the private fields by accessor methods. A non-static field can only be accessed if the inner class is non-static. For enums, the inner classes are however always static.

The later condition is what the compiler complains about:

Cannot make a static reference to the non-static field someField

You are trying to access a non-static field from a static inner class. This is not possible even though the field would be technically visible because of the inner class semantics. You could instruct the compiler explicitly to access the value by reading it from the super class by for example:

public String doIt() {
  MyEnum thiz = this;
  return thiz.someField;
}

Now the compiler knows that you are trying to access a member of a visible (outer) type instead of erroneously accessing the someField field of the (non-static) outer class instance (which does not exist). (Similarly, you could write super.someField which expresses the same idea that you want to go down the inheritance chain and not access an outer instance's field.) The easier solution would however be to simply make the field protected. This way the compiler is happy about the inheritance visibility and compiles your original setup.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
5

If you make someField protected instead of private or use super.someField instead you will be able to access it.

Alex - GlassEditor.com
  • 14,957
  • 5
  • 49
  • 49
  • 1
    I don't see anything wrong with it (in fact whoever downvoted has removed the downvote). That's really strange that super.someField works too. – Tim B Jul 29 '14 at 08:12
  • 1
    +0 Why does this solution work? – Kevin Jul 29 '14 at 18:40
2

someField is private, remove the private modifier or move it into your abstract classes.

Tim B
  • 40,716
  • 16
  • 83
  • 128
2

Private fields are not accessible from subclasses which is exactly what you do when you implement the MyEnum.doIt() abstract method on a per-instance basis. Change it to protected, and it will work.

icza
  • 389,944
  • 63
  • 907
  • 827
  • Am I the only one for whom it's not obvious that this will make the enum be an abstract class without an abstract identifier and creating an implementation of the abstract method will *implicitly* subclass the original enum type? It makes sense, but it's not obvious at all at first glance. – EpicPandaForce Jul 29 '14 at 08:14
  • @Zhuinden that's a standard feature of Java enums and one of the things that makes them so powerful. It's not obvious but all the good Java enum tutorials/docs cover it. – Tim B Jul 29 '14 at 08:18
1

someField is a private variable when enums are static variables. You cant assign non static variable to static variable in this way.

  • Yeah, but why does it work when you remove the `private` modifier? – Konstantin Yovkov Jul 29 '14 at 08:12
  • You can use non-static variables in enums, if you use a private constructor to initialize them, for example `public class MyEnum { FIRST("String"); private String myString; private MyEnum(String string) { this.string = string; }` – EpicPandaForce Jul 29 '14 at 08:13
  • I do not know why would you want to restrict instantiation of the class @Zhuinden... It can be useful in singleton pattern when you want to restrict number of instances of that class... But in this case I would not agree with you – Szymon Krawczyk Jul 29 '14 at 09:45
1

Apparently the problem is that when you say:

public enum MyEnum {
    ...
    public abstract String doIt();
} 

It implicitly needs the enum to be an abstract "class", as you must provide an implementation for it. Therefore, when you say

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

It gives an error because you are trying to access the "base class" MyEnum's private field, and as it is private, it is not visible from the implicitly created anonymous subclass. As such, protected is visible from the subclass, therefore it fixes the problem.

There are some questions on Stack Overflow that talk about this problem, such as Singletons, Enums and anonymous inner classes or Why can I anonymously subclass an enum but not a final class? .

EDIT: Apparently not everything in this statement is correct, because while this.someField doesn't work as the field is not visible from the subclass, it is visible accessed as super.someField. This is a phenomenon I've not seen before, and will try to look into now.

Community
  • 1
  • 1
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • Anonymous inner classes can see private fields. See http://stackoverflow.com/questions/25011061/why-can-enum-implementations-not-access-private-fields-in-the-enum-class – Tim B Jul 29 '14 at 08:26
  • 1
    The compiler tries to guess your intend. By writing `super`, you express to the compiler that you do not believe that the field was inherited such that it does not complain. See my answer below. – Rafael Winterhalter Jul 29 '14 at 08:48
1

What you could do is the following:

public enum MyEnum {
    FIRST,SECOND;

    private String someField;

    public String doIt(){
        switch(this){
            case FIRST:  return "1: " + someField; break;
            case SECOND: return "2: " + someField; break;
        }
    }

}

This way, you still inherit Enum and you can use MyEnum.values() and other perks that come from deriving Enum.

Zaq
  • 1,348
  • 1
  • 11
  • 19
  • 1
    yeah but you miss out on the strategy pattern, also, what happens if there are 2 dozen enums? that's gonna be an ugly, unmaintainable switch quickly! – edthethird Jul 29 '14 at 19:26
  • @ed The accepted answer uses more code per enumeration and isn't a proper enum. It seems to me that that would be less maintainable than my solution. Using a switch allows you to utilize fall-through and you don't need to rewrite method headers. – Zaq Jul 29 '14 at 21:26
  • true, but with IDE autocompletion a lot of the boilerplate can be filled out with a right click. I mean I agree, it is verbose, but that's the nature of Java. Also, another "benefit" (in quotes because that's debatable) it that enums are actually compiled into objects, and not treated as primitives. So you can do stuff like this in Java, and not many other languages. – edthethird Jul 30 '14 at 14:55
0

I wouldn't implement Strategy pattern using an enumeration. All code ends up in the same unti (file).

The idea is to separate the code. Use an interface as base class and then implement each Strategy as a separate sub-class. Nice a clean.

Roger C S Wernersson
  • 6,362
  • 6
  • 34
  • 45
  • 1
    Sometime it is better to have all things together. With enum you can see all posibility of the API at one place. But with Java 8 and clousours you can do it simply. – Mark Aug 06 '15 at 10:18
  • I would maybe use Enum for State Machines, if the states were simple, but for Strategy I would definitively go for individual classes. – Roger C S Wernersson Aug 12 '15 at 10:27