简体   繁体   中英

Java - Use enum values inside itself

We are prototyping a game for android devices using libgdx. For the screen management we are using a enum with the Screen names to get a abstraction layer for the ScreenManager Class. So you can do something like this to show a new screen and hide the old screen: ScreenManager.getInstance().show( Screens.LOGIN );

Now we want to use the back button of the device to jump back through the screens or exit the app. So we wanted to create a field inside each enum value to declare the parentScreen and use it if the back button is pressed. Unfortunately its not possible to use enum own fields inside itself and we get the following error: Cannot refer to the static enum field Screens.LOGIN within an initializer on protected Screens parentScreen = Screens.LOGIN; . Maybe anyone has a idea how this can be solved.

Here is the actual enum:

/**
 * Used to hide actual implementations of Screen interface and expose only "pointer objects".
 * All of them are declared with package-private class modifier.
 */
public enum Screens {

SPLASH {
    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new SplashScreen( game );
    }
},

LOGIN {
    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new LoginScreen( game );
    }
},

GAME {
    protected Screens parentScreen = Screens.LOGIN;

    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new GameScreen( game );
    }
},

CREDITS {
    protected Screens parentScreen = Screens.LOGIN;

    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new CreditsScreen( game );
    }
};

protected Screens parentScreen = null;

/** Every enum member musts override this method and it will be visible only inside the package */
protected abstract Screen getScreenInstance( MyGdxGame game );
}

This function should show the screens parent:

/**
 * Execute when the user clicks the back button
 * Default called by the AbstractScreen Class
 */
public void backButton() {
    this.show( this.currentScreen.parentScreen );
}

Thanks for your advices in advance!

There are multiple possibilities. My favourite would be to use a Constructor, because its less text, but a little easier to read:

public enum Screens {

SPLASH(Screens.LOGIN) {
    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new SplashScreen( game );
    }
},

LOGIN(null) {//...}

;//this semi-colon is expected after the enum values

private final Screens parentScreen;

Screens(Screens parent){//Constructor
    parentScreen = parent;
}

public Screens getParentScreen(){
    return parentScreen;
}

protected abstract Screen getScreenInstance( MyGdxGame game );

Another solution is to declare an abstract method getParentScreen():

public enum Screens {

SPLASH {
    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new SplashScreen( game );
    }
    @Override
    protected Screen getParentScreen() {
        return Screens.LOGIN;
    }
},
//...
;

protected abstract Screen getScreenInstance( MyGdxGame game );
protected abstract Screen getParentScreen();

You can try with this:

/**
 * Used to hide actual implementations of Screen interface and expose only "pointer objects".
 * All of them are declared with package-private class modifier.
 */
public enum Screens {

SPLASH {
    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new SplashScreen( game );
    }
},

LOGIN {
    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new LoginScreen( game );
     }
},

GAME {
    @Override
    protected Screen getParentScreen() {
       return Screens.LOGIN;
    } 

    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new GameScreen( game );
    }
},

CREDITS {

    @Override
    protected Screen getParentScreen() {
       return Screens.LOGIN;
    } 


    @Override
    protected Screen getScreenInstance( MyGdxGame game ) {
        return new CreditsScreen( game );
    }
};

protected Screen getParentScreen() {
    return null;
} 

/** Every enum member musts override this method and it will be visible only inside the package */
protected abstract Screen getScreenInstance( MyGdxGame game );
}

By using a method (instead of a field) you overcome the limitation.

NOTE Also, doing something like:

public enum X {
  VALUE1 {
     protected String fieldYYY = ...;
  }

  protected String fieldYYY = ...;
}

is not really a nice thing to do. When enums are compiled they get roughly translated to an abstract super class X with concrete subclasses X.VALUE1, X.VALUE2, X.VALUEN... If you declare a field in your superclass (X) and then declare it again in your subclass you are not really overwritting it but you end up with 2 fields, with the same name... the first one (the one in X, the superclass) hidden by scope... but it is there! If such fields are mutable - not the case with enums though - you might unleash mayhem (some pieces of code referencing the one in your superclass, others the one you have)!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM