13

I have the following enum:

 enum Days{
    TODAY{
        @Override
        public Date getLowerBound(){
            another();             //1
            currentUpperBound();   //2
            return null;
        }

        @Override
        public Date another() {
            return null;
        }
    };

    public abstract Date getLowerBound();

    public abstract Date another();

    private Date currentUpperBound(){
        return null;
    }
}

Why does //2 causes compile-time error with

Cannot make a static reference to the non-static method 
currentUpperBound() from the type Days

But //1 compiles fine? Both methods are non-static. I can't see any problem... Maybe it has something to do with Eclipse?

UPDATE: As @Florian Schaetz noticed in the comment, if we declare the method having static private modifier it will work fine. Why?

St.Antario
  • 26,175
  • 41
  • 130
  • 318

3 Answers3

9

I suggest making currentUpperBounds() protected instead of private. Another solution would be to prefix your call with super., which also works:

@Override
public Date getLowerBound(){
   another();
   super.currentUpperBound();
   return null;
}

Alternatively, TODAY also works:

@Override
public Date getLowerBound(){
   another();
   TODAY.currentUpperBound();
   return null;
}

Mick Mnemonic mentioned the excellent answer in this duplicate, which explains it pretty nicely.

Community
  • 1
  • 1
Florian Schaetz
  • 10,454
  • 5
  • 32
  • 58
  • I understood the problem, yes. I'd make them having default access modifier. Tahnk you. – St.Antario Aug 11 '15 at 08:34
  • I am wondering why the error is totally unrelated. Though changing it to `protected` or `public` fixes it. Is it related to static context for some reason. **And changing the method to static fixes the error too...:O** – Codebender Aug 11 '15 at 08:36
  • @Codebender Me too, but I presume that's eclipse all along. – St.Antario Aug 11 '15 at 08:37
  • 1
    Technically, defining the method as static also works. Seems that TODAY can access a static private, but not a non-static private. Interesting, will have to do some reading about that. – Florian Schaetz Aug 11 '15 at 08:38
  • @FlorianSchaetz Indeed, interesting. Why does the static private method work? – St.Antario Aug 11 '15 at 08:39
4

TL;DR

Solution to your problem

I suggest making the currentUpperBound() method protected.

Why?

You should consider each value of your enum as an anonymous subclass of Days. Moreover, since the enum must guarantee each of its value is unique (so that you can test equality with == for instance), it is stored as a static final field (hence also the habit of naming enum values with capital letters.

Understanding the enum behind the scene

Code equivalence

To show some code on how an enum works1:

public enum MyEnum {
    JESSE {
       @Override
       public void sayMyName() {
           System.out.println("Pinkman");
       }
    }, WALTER;

    public void sayMyName() {
        System.out.println("Heisenberg");
    }

    private void unreachableMethod() {
        // try again
    }
}

is (almost) equivalent to:

public class MyEnum {
    private static final MyEnum JESSE = new MyEnum() {
       @Override
       public void sayMyName() {
           System.out.println("Pinkman");
       }
    };
    private static final MyEnum WALTER = new MyEnum();

    public void sayMyName() {
        System.out.println("Heisenberg");
    }

    private void unreachableMethod() {
        // try again
    }
}

When you write it this way, some things become much easier to understand:

- why == works for testing enum equality

Only one pointer per value. Object's equals(Object) is perfect here as it tests only that.

- why you can inherit methods from MyEnum or access private static ones, but not private non-static methods

  • The values are stored as static fields so they cannot access instance context.
  • The value inherit MyEnum so anything accessible through legacy is visible to them.

Why did I say that is was almost equivalent?

There are some additional controls embedded within the enum mechanism.

For instance, the switch is able to tell whether you had a case for each value for an enum: try testing only some values and not defining a default, the compiler will raise a warning. This would be impossible if you were simply using an abstract class with constants.

The values() method also shows things are more evolved than my simple example, but I think this helps understanding.


1 based on my understanding of the JSL

Chop
  • 4,267
  • 5
  • 26
  • 58
  • But that doesn't provuide the reason for the static private. – St.Antario Aug 11 '15 at 08:43
  • 2
    The JSL says, that the enum body is a anonymous class, not a static one. But probably has to do something with that. – Florian Schaetz Aug 11 '15 at 09:37
  • @FlorianSchaetz I thought of it while eating my lunch and think I understand a little better how it works behind the scene. I revamped my answer and would be interested in picking your brain about it. – Chop Aug 11 '15 at 10:31
  • @St.Antario I revamped my answer and think it answers all the questions now. Hope this helps. – Chop Aug 11 '15 at 10:32
  • 2
    I think I found another funny detail: You CAN actually call the private non-static method. You only have to use `super.` . So in your example `super.unreachableMethod()` in the overriding `sayMyName`method works. And before the whole thing gets to easy... Prefixing it with `MyEnum.` also works. Funny, but slowly I realize how fragmentary my understanding of Java is, especially with the details. – Florian Schaetz Aug 11 '15 at 10:57
  • @FlorianSchaetz Nice catch! I did a quick try: this works with standard classes too. E.g. `class A { private static A a = new A() {/* override sth here */}; }`: the inner class can access the private methods of the parent with `super`. This obviously works only because the anonymous class is defined within the scope of its parent. It appears to be a bit of a quirk and I would not rely on this in case it gets changed in future versions. – Chop Aug 11 '15 at 11:04
  • 2
    See the excellent answer in the [duplicate question](http://stackoverflow.com/questions/28964926/is-an-enum-constant-specific-class-body-static-or-non-static) for more details. – Mick Mnemonic Aug 11 '15 at 11:23
3

You can either declare the currentUpperBound method as abstract and implement it in your instances, or leave it implemented.

In both cases, you should declare it with a non-private access modifier.

The compiler error message here is quite confusing. It assumes currentUpperBound should be static, which would grant you access from the instances even if the method is declared as private.

Mena
  • 47,782
  • 11
  • 87
  • 106