10

I'm having a strange issue which I'm not sure if it's a compiler problem or my understanding of enums with interfaces. I'm using IntelliJ IDEA 12, building an Android project, and I have a class like this:

public class ClassWithEnum {
    private MyEnum myEnum;

    //Trying to access it internally here throws the error
    public boolean isActionable() {
        return myEnum.isActionable();
    }

    public enum MyEnum implements Action {
        ACTIONABLE() {
            @Override
            public boolean isActionable() { return true; }
        },
        NOT_ACTIONABLE() {
            @Override
            public boolean isActionable() { return false; }
        }
    }

    public interface Action {
        public boolean isActionable();
    }
}

Now, this was working initially, but now the compiler is complaining (and I've tried this in a brand new project as well with the same results) with the error:

java: /Users/kcoppock/Documents/Projects/EnumInterfaceTest/src/com/example/EnumInterfaceTest/ClassWithEnum.java:11: cannot find symbol
symbol  : method isActionable()
location: class com.example.EnumInterfaceTest.ClassWithEnum.MyEnum

I've done this before (enumerations with behaviors defined by an interface) with no issues. Any thoughts?

Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274

5 Answers5

8

You need to implement isActionable() method in MyEnum itself. Because the method isActionable() defined inside the ACIONABLE and NOT_ACTIONABLE are local to them . So you need the global method for the MyEnum enum .

use this code instead:

public enum MyEnum implements Action {
        ACTIONABLE() {
            @Override
            public boolean isActionable() { return true; }
        },
        NOT_ACTIONABLE() {
            @Override
            public boolean isActionable() { return false; }
        };
        @Override
        public boolean isActionable() { return false;}
    }
Vishal K
  • 12,976
  • 2
  • 27
  • 38
  • This seems reasonable, and it was something I considered, but I don't believe it's actually necessary provided the enumerations actually implement the interface. Changing the compiler to Eclipse then back to javac cleared the issues and it runs with no issues. – Kevin Coppock Jan 28 '13 at 20:49
  • Actually I'm wrong, it just wasn't recompiling after the change to Eclipse. It still fails without adding the global override. After adding that, it's able to compile as expected. – Kevin Coppock Jan 28 '13 at 21:01
  • Yes it did. Adding the global method as you suggested causes the compiler to succeed. I'm curious if it's actually a javac bug, or just a more strict requirement in that version. – Kevin Coppock Jan 28 '13 at 21:04
  • What makes you think it to be a java bug?? ACTIONABLE is like an object which is member of MyEnum class . Literally it is equivalent to `public static final MyEnum ACTIONABLE = new MyEnum(){ @Override public boolean isActionable(){return true;}; ` So untill and unless MyEnum implements isActionable() method , this method will be considered as local for ACTIONABLE object. You can't even access this method using the ACTIONABLE object itself if this method is not implemented by MyEnum itself. – Vishal K Jan 28 '13 at 21:12
  • 1
    A `javac` bug, not a java bug. The fact that it compiles and runs fine under one version of the compiler, but not another, implies either a bug or change of requirements. If it were not actually overriding the interface method (since it includes the annotation @Override) the compiler would complain that it is not actually overriding a superclass method. Since it's not complaining, I assume that it is in fact accessible from the local object. – Kevin Coppock Jan 28 '13 at 21:14
  • yes got a point ... Can u tell me under which version of compiler is it compiling fine? – Vishal K Jan 28 '13 at 21:17
  • It compiles fine with the `Eclipse` compiler, or as @JBNizet mentions below, with `javac` 1.7. – Kevin Coppock Jan 28 '13 at 21:18
  • Ok..Thanks i would go look for them. :) – Vishal K Jan 28 '13 at 21:19
  • 1
    Great answer. I would point out you can also keep the method abstract and not implement its body in the interface: – voho Jun 24 '14 at 11:24
3

You could try this alternative:

   public enum MyEnum implements Action {
        ACTIONABLE(true), 
        NOT_ACTIONABLE(false);

        private final boolean actionable;

        MyEnum(boolean actionable) {
           this.actionable = actionable;
        }

        @Override
        public boolean isActionable() { 
           return this.actionable; 
        }
    }
Adrián
  • 6,135
  • 1
  • 27
  • 49
  • 1
    That's a workable solution too. Unfortunately the real class I'm using it with has some more complex functionality with it, but still good info. :) – Kevin Coppock Jan 28 '13 at 20:47
2

That looks like a javac bug. javac compiles it fine in JDK7. It doesn't in JDK6 (javac 1.6.0_24), both from IntelliJ and from the command line.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
1

It seems that javac for 1.6 requires a global override for the interface method, whereas later versions do not, nor does the Eclipse compiler.

Seems it was just a bug with IntelliJ. I changed the compiler setting from javac to Eclipse, then back to javac and it compiles and runs as expected.

False alarm. :)

Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
  • 1
    It's a javac bug in fact. EVen from the command line, javac doesn't compile it. – JB Nizet Jan 28 '13 at 20:55
  • @JBNizet Interesting, so you can reproduce it too? It's working for me with the javac compiler now, though, after changing. Let me try to fully recompile and see if it fails. – Kevin Coppock Jan 28 '13 at 20:56
  • 1
    Yes, I can reproduce it. Even if the Action interface and the Enum are promoted to top-level classes, it doesn't compile. – JB Nizet Jan 28 '13 at 20:58
  • It appears both you and @VishalK are correct. If I rebuild with javac, it fails again. If I rebuild with Eclipse it compiles. If I add the default action as Vishal suggests, it compiles with javac. – Kevin Coppock Jan 28 '13 at 20:58
1

Some people already answered this perfectly, I just add one more little tip: you can also keep the method abstract in the interface:

public enum MyEnum implements Action {
    ACTIONABLE() {
        @Override
        public boolean isActionable() { return true; }
    },
    NOT_ACTIONABLE() {
        @Override
        public boolean isActionable() { return false; }
    };
    @Override
    abstract public boolean isActionable();
}
voho
  • 2,805
  • 1
  • 21
  • 26