Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Definition of methods and variables inside enum's constant

I was doing some experiments and accidently wrote a code, which is very weird and I don't get it all. I was even surprised that I can compile it. It looks like this:

enum Foo {
    VALUE_1 {
        public int myVariable = 1;
    },
    VALUE_2 {
        public void myMethod() {
            //
        }
    },
    VALUE_3;
}

As expected, it's not possible to access such an element in the following way:

Foo.VALUE_2.myMethod();

The reason is, that compiler will look for that method inside the enumeration itself.

I presumed that it's not possible to access these methods and variables from outside the enumeration. For this reason, I tried to create a parametric constructor and call it with some internal variable:

enum Foo {
    VALUE(internalVariable) {
        int internalVariable = 1;
    };

    private Foo(int param) {
        //
    }
}

It wasn't possible to compile such a construction. Now I was thinking what's the point of defining something inside the constant if there is no way to access it.

I was trying to create the same-named methods in the constant as well in the enumeration itself to check out if it collides in some way. It didn't!

enum Foo {
    VALUE_1 {
        int myVariable = 1;

        public int myMethod() {
            return myVariable;
        }
    },
    VALUE_2 {
        //
    };

    public int myMethod() {
        return 0;
    }
}

And here comes the funny moment! I tried to proceed call of myMethod() inside the enumeration and actually figured out how this Java magic works. Methods, which are defined inside the constant, overrides methods defined inside the enumeration.

Foo.VALUE_1.myMethod(); // Returns 1
Foo.VALUE_2.myMethod(); // Returns 0

However, we can't override variable, right? So I was curious, how it works with variables only.

enum Foo {
    VALUE_1 {
        public int myVariable = 1;
    },
    VALUE_2 {
        //
    };

    public int myVariable = 0;
}

....

System.out.println(Foo.VALUE_1.myVariable); // Returns 0
System.out.println(Foo.VALUE_2.myVariable); // Returns 0

Now I'm finally getting to my questions:

  1. Why I don't get any error if I create public method inside the constant and left enumeration empty without this method? In that case, the method I just defined can't be called at all. Or am I wrong?

    Update: I know that enumeration can implement interface. However, if I haven't specifically said that, whole code is pointless.

    Someone pointed out that even if method can't be accessed from the language in the normal way, it's still possible to use reflection. Well... Why don't we design an inaccessible keyword?

    inaccessible void magicalMethod() {
         //
    }
    

    Such a method will be compiled into the *.class file. When you want to use it, you've to load bytecode by yourself and interpret it.

    I just can't understand, why it's possible to define unreachable method. The only reason I can think is that programmer is working and doesn't have definition of interface yet. So he's just preparing code of single methods and will add "implements" keyword later. Beside this is illogical, it would still require to have such a method in all constants.

    I think this should end up with error, not just warning about unused method. You may forget to add "implement" clause or to define method in the enumeration (which would be overridden) and will realize that just after the first use. Java is very strict language, so I'd expect this behavior.

  2. Why I don't get any error if I create public variable (or field, to be more precise) inside the constant? It can't be accessed in the any case (from the outside). Therefore, modifier "public" doesn't make any sense here.

    Update: It's more less the same thing as in the previous point, except the visibility modifier is completely useless here. It really doesn't matter if it's public, protected or private, because you won't be able to access that anyway. I think this is a bug.

  3. Why it's possible to define a class (without visibility modifiers), but not interface? Yeah, you wouldn't probably want to write so brutal enumeration that you would need to define classes inside the constant and even to use inheritance there. But if it's possible to define classes and abstract classes, it seems little weird.

    Update: This is definitely not something you'd need on regular basis, but I understand that it might be useful. But why it's limited to classes only and interfaces can't be defined as well?

    enum Foo {
        VALUE {
            class MyClass {
                // OK
            }
    
            abstract class MyAbstractClass {
                // OK
            }
    
            interface MyInterface {
                // FAIL. It won't compile.
            }
        }
    }
    
  4. Did you use such a functionality somewhere? I can imagine it might be useful, but it's little confusing. Also, when I was searching for some resources about that, I didn't find anything.

    Update: I'd like to see some practical example wth overridden methods in an enum constant class body. Have you seen it in some open-source project?

Environment:

$ java -version
java version "1.7.0_21"
OpenJDK Runtime Environment (IcedTea 2.3.9) (7u21-2.3.9-0ubuntu0.12.10.1)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)

Thanks for your time and for your answers!

like image 212
tzima Avatar asked Aug 23 '13 00:08

tzima


People also ask

Can we define constructors methods and variables inside enum?

Enumerations are similar to classes and, you can have variables, methods, and constructors within them. Only concrete methods are allowed in an enumeration.

Can you have methods in an enum?

The enum class body can include methods and other fields. The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared.

Can enums be defined inside interface?

It's perfectly legal to have an enum declared inside an interface . In your situation the interface is just used as a namespace for the enum and nothing more. The interface is used normally wherever you use it.

Which of the following method can be used to get enum constants?

All the constants of an enum type can be obtained by calling the implicit public static T[] values() method of that type.


1 Answers

OK, I've actually used this functionality! I'm writing simple game and want to provide two sound packs. Because the game is very simple and won't be probably extended in the future, I didn't want to create some complex mechanism for achieving such a thing.

public enum SoundPack {
    CLASSICAL {
        @Override
        public String getSoundPickUp() {
            return "res/sounds/classical/pick.wav";
        }

        @Override
        public String getSoundNewLevel() {
            return "res/sounds/classical/success.wav";
        }

        @Override
        public String getSoundFail() {
            return "res/sounds/fail.wav";
        }
    },
    UNHEALTHY {
        @Override
        public String getSoundPickUp() {
            return "res/sounds/unhealthy/quick_fart.wav";
        }

        @Override
        public String getSoundNewLevel() {
            return "res/sounds/unhealthy/toilet_flush.wav";
        }

        @Override
        public String getSoundFail() {
            return "res/sounds/unhealthy/vomiting.wav";
        }
    };

    public abstract String getSoundPickUp();
    public abstract String getSoundNewLevel();
    public abstract String getSoundFail();
}

So, I've just defined the enum you see above. In the class, which is holding all configuration, is just one attribute like this:

private SoundPack soundPack = SoundPack.CLASSICAL;

Now, if I need to play some sound, I can get the path very simply:

configuration.getSoundPack().getSoundNewLevel();

Configuration might be changed very easily runtime just by assigning another value to the field soundPack. If sound isn't loaded yet (and there's chance they won't be as I'm using lazy loading a lot), changes will take affect immediately. Without changing anything else.

Also, if I'd want to add new sound pack, it would be done by defining new constant in that enum. Eclipse will show warning, I'll just press CTRL+1 and generate these methods. So it's also very easy.

I know that this is not the best way how to do that. But it's easy, it's quick and what's the most important: I wanted to try to use that in praxis. :-)

like image 121
tzima Avatar answered Sep 19 '22 12:09

tzima