Metodi Java Enum: restituisce enum in direzione opposta


113

Vorrei dichiarare una enum Direction, che ha un metodo che restituisce la direzione opposta (il seguente non è sintatticamente corretto, cioè, le enumerazioni non possono essere istanziate, ma illustra il mio punto). È possibile in Java?

Ecco il codice:

public enum Direction {

     NORTH(1),
     SOUTH(-1),
     EAST(-2),
     WEST(2);

     Direction(int code){
          this.code=code;
     }
     protected int code;
     public int getCode() {
           return this.code;
     }
     static Direction getOppositeDirection(Direction d){
           return new Direction(d.getCode() * -1);
     }
}

Usa solo un interruttore. Hai solo 4 casi.
Sotirios Delimanolis

12
A proposito, d.getCode() * -1==-d.getCode()
tckmn

Il capitolo 6 (almeno nella 2E) di Java efficace di Bloch potrebbe essere interessante e altamente raccomandato.
jedwards

ntu.edu.sg/home/ehchua/programming/java/javaenum.html , la sezione 2.1 ha spiegato chiaramente il concetto
vikramvi

Risposte:


207

Per coloro che sono attirati qui dal titolo: sì, puoi definire i tuoi metodi nella tua enumerazione. Se ti stai chiedendo come invocare tale metodo non statico, lo fai allo stesso modo di qualsiasi altro metodo non statico: lo invochi su un'istanza di tipo che definisce o eredita quel metodo. In caso di enum tali istanze sono semplicemente ENUM_CONSTANTs.

Quindi tutto ciò di cui hai bisogno è EnumType.ENUM_CONSTANT.methodName(arguments).


Ora torniamo al problema dalla domanda. Una delle soluzioni potrebbe essere

public enum Direction {

    NORTH, SOUTH, EAST, WEST;

    private Direction opposite;

    static {
        NORTH.opposite = SOUTH;
        SOUTH.opposite = NORTH;
        EAST.opposite = WEST;
        WEST.opposite = EAST;
    }

    public Direction getOppositeDirection() {
        return opposite;
    }

}

Ora Direction.NORTH.getOppositeDirection()tornerà Direction.SOUTH.


Ecco un modo un po 'più "hacky" per illustrare il commento @jedwards, ma non sembra flessibile come il primo approccio poiché l'aggiunta di più campi o la modifica del loro ordine interromperà il nostro codice.

public enum Direction {
    NORTH, EAST, SOUTH, WEST;

    // cached values to avoid recreating such array each time method is called
    private static final Direction[] VALUES = values();

    public Direction getOppositeDirection() {
        return VALUES[(ordinal() + 2) % 4]; 
    }
}

3
Stavo per inventare un .values()[ordinal()]trucco, ma questo approccio è molto più robusto
jedwards

come lo usi, però? e come si chiama questa tecnica?
Thufir

1
@Thufir " come lo usi " se chiedi del metodo, quindi come qualsiasi altro metodo - lo invochi su istanza di classe con questo metodo. Le istanze di Directionclasse enum sono NORTH, EAST, SOUTH, WESTquindi si può semplicemente utilizzare NORTH.getOppositeDirection()e tornerà SOUTH. " come si chiama questa tecnica? " se lo chiedi static{...}allora è un blocco di inizializzazione statico , è il codice invocato quando la classe viene caricata per la prima volta (fa parte dello stesso processo che inizializza i campi statici).
Pshemo

@ Pshemo, mi chiedo come sarà la versione Spring del codice sopra, se i valori che vengono impostati nel blocco statico devono essere iniettati dal file delle proprietà, ad esempio.
Vikas Prasad

161

Per una piccola enumerazione come questa, trovo che la soluzione più leggibile sia:

public enum Direction {

    NORTH {
        @Override
        public Direction getOppositeDirection() {
            return SOUTH;
        }
    }, 
    SOUTH {
        @Override
        public Direction getOppositeDirection() {
            return NORTH;
        }
    },
    EAST {
        @Override
        public Direction getOppositeDirection() {
            return WEST;
        }
    },
    WEST {
        @Override
        public Direction getOppositeDirection() {
            return EAST;
        }
    };


    public abstract Direction getOppositeDirection();

}

8
Grande idea! Ciò è utile anche quando in genere si desidera che ogni valore enum abbia un comportamento specifico.
OferBr

28

Funziona:

public enum Direction {
    NORTH, SOUTH, EAST, WEST;

    public Direction oppose() {
        switch(this) {
            case NORTH: return SOUTH;
            case SOUTH: return NORTH;
            case EAST:  return WEST;
            case WEST:  return EAST;
        }
        throw new RuntimeException("Case not implemented");
    }
}

8
Piuttosto che restituire null, una clausola predefinita che genera un'adeguata RuntimeException potrebbe essere migliore per indicare che si è verificato un errore del programmatore nel non definire un contrario per una nuova direzione aggiunta.
Timothy055

1
Ciò richiede che il chiamante gestisca null. Richiede inoltre al manutentore di assicurarsi di aggiungere un caso ogni volta che viene aggiunto un nuovo tipo di direzione. Vedi la risposta di Amir Afghani sull'uso di un metodo astratto che può essere sovrascritto da ogni valore enum, in questo modo non rischi mai di perderne uno e non devi preoccuparti di gestire null.
Michael Peterson

14

Crea un metodo astratto e fai in modo che ciascuno dei tuoi valori di enumerazione lo sovrascriva. Poiché sai il contrario mentre lo crei, non è necessario generarlo o crearlo dinamicamente.

Non si legge bene però; forse switchsarebbe più gestibile?

public enum Direction {
    NORTH(1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.SOUTH;
        }
    },
    SOUTH(-1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.NORTH;
        }
    },
    EAST(-2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.WEST;
        }
    },
    WEST(2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.EAST;
        }
    };

    Direction(int code){
        this.code=code;
    }
    protected int code;

    public int getCode() {
        return this.code;
    }

    public abstract Direction getOppositeDirection();
}

4

Sì, lo facciamo sempre. Restituisci un'istanza statica anziché un nuovo oggetto

 static Direction getOppositeDirection(Direction d){
       Direction result = null;
       if (d != null){
           int newCode = -d.getCode();
           for (Direction direction : Direction.values()){
               if (d.getCode() == newCode){
                   result = direction;
               }
           }
       }
       return result;
 }

0
public enum Direction {
    NORTH, EAST, SOUTH, WEST;

    public Direction getOppositeDirection(){
        return Direction.values()[(this.ordinal() + 2) % 4];
    }
}

Gli enum hanno un metodo di valori statici che restituisce un array contenente tutti i valori dell'enumerazione nell'ordine in cui vengono dichiarati. fonte

poiché NORTH ottiene 1, EAST ottiene 2, SUD ottiene 3, WEST ottiene 4; puoi creare una semplice equazione per ottenere quella opposta:

(valore + 2)% 4


2
perché questa è la risposta? Come ti aspetti che questo aiuti i futuri lettori, o chiunque altro, ad imparare senza alcuna spiegazione?
GrumpyCrouton

Sebbene questo codice possa rispondere alla domanda, fornire un contesto aggiuntivo su come e / o perché risolve il problema migliorerebbe il valore a lungo termine della risposta. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo alla persona che lo chiede ora! Modifica la tua risposta per aggiungere una spiegazione e fornire un'indicazione di quali limitazioni e ipotesi si applicano. Inoltre, non fa male menzionare perché questa risposta è più appropriata di altre.
ItamarG3

è difficile leggere il codice senza commenti? o hai bisogno di un'esclusiva javadoc per 7 righe di codice?
Pregunton,

1
Questa soluzione è fragile in quanto dipende dall'ordinamento dei valori enum. Se dovessi modificare l'ordine in modo che sia alfabetico, la tua equazione intelligente non fornirebbe più il contrario corretto.
Josh J
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.