Méthodes Java Enum - retourne une énumération dans la direction opposée

113

Je voudrais déclarer une direction enum, qui a une méthode qui renvoie la direction opposée (ce qui suit n'est pas syntaxiquement correct, c'est-à-dire que les énumérations ne peuvent pas être instanciées, mais cela illustre mon point). Est-ce possible en Java?

Voici le code:

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);
     }
}
Skyler
la source
Utilisez simplement un interrupteur. Vous n'avez que 4 cas.
Sotirios Delimanolis
12
À propos, d.getCode() * -1==-d.getCode()
tckmn
Le chapitre 6 (au moins dans 2E) de Effective Java de Bloch pourrait être intéressant et fortement recommandé.
jedwards
ntu.edu.sg/home/ehchua/programming/java/javaenum.html , la section 2.1 a bien expliqué le concept
vikramvi

Réponses:

207

Pour ceux qui sont attirés ici par leur titre: oui, vous pouvez définir vos propres méthodes dans votre énumération. Si vous vous demandez comment invoquer une telle méthode non statique, vous le faites de la même manière qu'avec toute autre méthode non statique - vous l'appelez sur une instance de type qui définit ou hérite de cette méthode. Dans le cas d'énumérations, ces instances sont simplement l' ENUM_CONSTANTart.

Donc, tout ce dont vous avez besoin est EnumType.ENUM_CONSTANT.methodName(arguments).


Revenons maintenant au problème à partir de la question. L'une des solutions pourrait être

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;
    }

}

Je Direction.NORTH.getOppositeDirection()reviendrai maintenant Direction.SOUTH.


Voici une manière un peu plus «hacky» d'illustrer le commentaire @jedwards mais cela ne semble pas aussi flexible que la première approche car l'ajout de plus de champs ou la modification de leur ordre cassera notre code.

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]; 
    }
}
Pshemo
la source
3
J'étais sur le point de créer un .values()[ordinal()]hack mais cette approche est beaucoup plus robuste
jedwards
comment l'utilisez-vous? et comment s'appelle cette technique?
Thufir
1
@Thufir " comment l'utilisez-vous " si vous posez des questions sur la méthode, alors comme toute autre méthode - vous l'appelez sur une instance de classe avec cette méthode. Les instances de la Directionclasse enum sont NORTH, EAST, SOUTH, de WESTsorte que vous pouvez simplement utiliser NORTH.getOppositeDirection()et il retournera SOUTH. " comment s'appelle cette technique? " si vous posez la question, static{...}alors c'est un bloc d'initialisation statique , c'est du code appelé lorsque la classe est chargée pour la première fois (cela fait partie du même processus qui initialise les champs statiques).
Pshemo
@Pshemo, je me demande comment la version Spring du code ci-dessus sera, si les valeurs qui sont définies dans le bloc statique doivent être injectées à partir du fichier de propriétés, par exemple.
Vikas Prasad
161

Pour une petite énumération comme celle-ci, je trouve que la solution la plus lisible est:

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();

}
Amir Afghani
la source
8
Bonne idée! Ceci est également utile lorsque vous souhaitez généralement que chaque valeur d'énumération ait un comportement spécifique.
OferBr
28

Cela marche:

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");
    }
}
Pikrass
la source
8
Plutôt que de renvoyer null, une clause par défaut qui lève une RuntimeException appropriée pourrait être préférable pour indiquer qu'il y a eu une erreur de programmeur en ne définissant pas un contraire pour une direction nouvellement ajoutée.
Timothy055
1
Cela nécessite que l'appelant gère null. Cela oblige également le responsable à s'assurer d'ajouter un cas à chaque fois qu'un nouveau type de direction est ajouté. Voir la réponse d'Amir Afghani sur l'utilisation d'une méthode abstraite qui peut être remplacée par chaque valeur enum, de cette façon vous ne risquez jamais d'en manquer une, et vous n'avez pas à vous soucier de la gestion de null.
Michael Peterson
14

Créez une méthode abstraite et faites en sorte que chacune de vos valeurs d'énumération la remplace. Puisque vous savez le contraire pendant que vous le créez, il n'est pas nécessaire de le générer ou de le créer dynamiquement.

Il ne lit pas bien cependant; peut-être un switchserait plus gérable?

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();
}
Makoto
la source
4

Oui, nous le faisons tout le temps. Vous renvoyez une instance statique plutôt qu'un nouvel objet

 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;
 }
BevynQ
la source
0
public enum Direction {
    NORTH, EAST, SOUTH, WEST;

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

Les énumérations ont une méthode de valeurs statiques qui renvoie un tableau contenant toutes les valeurs de l'énumération dans l'ordre dans lequel elles sont déclarées. la source

puisque NORTH obtient 1, EAST obtient 2, SUD obtient 3, WEST obtient 4; vous pouvez créer une équation simple pour obtenir l'inverse:

(valeur + 2)% 4

Pregunton
la source
2
pourquoi est-ce la réponse? Comment pensez-vous que cela aidera les futurs lecteurs, ou n'importe qui d'ailleurs, à apprendre sans aucune explication?
GrumpyCrouton
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire sur la façon et / ou pourquoi il résout le problème améliorerait la valeur à long terme de la réponse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant! Veuillez modifier votre réponse pour ajouter une explication et donner une indication des limites et des hypothèses applicables. Cela ne fait pas de mal non plus de mentionner pourquoi cette réponse est plus appropriée que d'autres.
ItamarG3
est-il difficile de lire le code sans commentaires? ou avez-vous besoin d'un javadoc exclusif pour 7 lignes de code?
Pregunton
1
Cette solution est fragile car elle dépend de l'ordre des valeurs d'énumération. Si vous deviez changer l'ordre pour qu'il soit alphabétique, votre équation intelligente ne fournirait plus le bon contraire.
Josh J