Métodos Java Enum: devuelve la enumeración en dirección opuesta

113

Me gustaría declarar una dirección de enumeración, que tiene un método que devuelve la dirección opuesta (lo siguiente no es sintácticamente correcto, es decir, no se pueden crear instancias de enumeraciones, pero ilustra mi punto). ¿Es esto posible en Java?

Aquí está el código:

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
fuente
Solo usa un interruptor. Solo tienes 4 casos.
Sotirios Delimanolis
12
Por cierto, d.getCode() * -1==-d.getCode()
tckmn
El capítulo 6 (en 2E, al menos) de Effective Java de Bloch puede ser de interés y muy recomendable.
Jedwards
ntu.edu.sg/home/ehchua/programming/java/javaenum.html , la sección 2.1 ha explicado el concepto claramente
vikramvi

Respuestas:

207

Para aquellos atraídos aquí por el título: sí, puede definir sus propios métodos en su enumeración. Si se pregunta cómo invocar dicho método no estático, hágalo de la misma manera que con cualquier otro método no estático: lo invoca en una instancia del tipo que define o hereda ese método. En el caso de enumeraciones, estos casos son simplemente ENUM_CONSTANTs.

Así que todo lo que necesitas es EnumType.ENUM_CONSTANT.methodName(arguments).


Ahora volvamos al problema de la pregunta. Una de las soluciones podría ser

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

}

Ahora Direction.NORTH.getOppositeDirection()volverá Direction.SOUTH.


Aquí hay una forma un poco más "hacky" de ilustrar el comentario de @jedwards, pero no se siente tan flexible como el primer enfoque, ya que agregar más campos o cambiar su orden romperá nuestro código.

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
fuente
3
Estaba a punto de azotar encima de un .values()[ordinal()]truco, pero este enfoque es mucho más robusto
Jedwards
¿Cómo lo usas? y ¿cómo se llama esta técnica?
Thufir
1
@Thufir " cómo se usa " si está preguntando sobre el método, entonces, como cualquier otro método, lo invoca en una instancia de clase con este método. Las instancias de Directionla clase de enumeración son NORTH, EAST, SOUTH, WESTpor lo que sólo puede utilizar NORTH.getOppositeDirection()y ha de devolver SOUTH. " ¿Cómo se llama esta técnica? " Si está preguntando acerca de static{...}entonces es un bloque de inicialización estático , se invoca el código cuando la clase se carga por primera vez (es parte del mismo proceso que inicializa los campos estáticos).
Pshemo
@Pshemo, me pregunto cómo será la versión Spring del código anterior, si los valores que se establecen en el bloque estático deben inyectarse desde el archivo de propiedades.
Vikas Prasad
161

Para una pequeña enumeración como esta, encuentro que la solución más legible es:

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
fuente
8
¡Gran idea! Esto también es bueno cuando generalmente desea que cada valor de enumeración tenga un comportamiento específico.
OferBr
28

Esto funciona:

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
fuente
8
En lugar de devolver un valor nulo, una cláusula predeterminada que arroje una RuntimeException adecuada podría ser mejor para indicar que hubo un error del programador al no definir un opuesto para una dirección recién agregada.
Timothy055
1
Esto requiere que la persona que llama maneje nulo. También requiere que el encargado de mantenimiento se asegure de agregar un caso cada vez que se agrega un nuevo tipo de dirección. Vea la respuesta de Amir Afghani sobre el uso de un método abstracto que puede ser anulado por cada valor de enumeración, de esa manera nunca se arriesga a perderse uno y no tiene que preocuparse por manejar nulos.
Michael Peterson
14

Cree un método abstracto y haga que cada uno de sus valores de enumeración lo anule. Como sabe lo contrario mientras lo crea, no es necesario generarlo o crearlo dinámicamente.

Sin embargo, no se lee bien; tal vez switchsería más manejable?

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
fuente
4

Sí, lo hacemos todo el tiempo. Devuelve una instancia estática en lugar de un nuevo objeto

 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
fuente
0
public enum Direction {
    NORTH, EAST, SOUTH, WEST;

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

Las enumeraciones tienen un método de valores estáticos que devuelve una matriz que contiene todos los valores de la enumeración en el orden en que se declaran. fuente

dado que NORTE obtiene 1, ESTE obtiene 2, SUR obtiene 3, OESTE obtiene 4; puedes crear una ecuación simple para obtener la opuesta:

(valor + 2)% 4

Pregunton
fuente
2
¿ Por qué es esta la respuesta? ¿Cómo espera que esto ayude a los futuros lectores, oa cualquier otra persona, a aprender sin ninguna explicación?
GrumpyCrouton
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre cómo y / o por qué resuelve el problema mejoraría el valor de la respuesta a largo plazo. Recuerde que está respondiendo la pregunta para los lectores en el futuro, ¡no solo la persona que pregunta ahora! Edite su respuesta para agregar una explicación y dé una indicación de las limitaciones y suposiciones que se aplican. Tampoco está de más mencionar por qué esta respuesta es más apropiada que otras.
ItamarG3
¿Es difícil leer el código sin comentarios? ¿O necesitas un javadoc exclusivo para 7 líneas de código?
Pregunton
1
Esta solución es frágil ya que depende del orden de los valores de enumeración. Si cambiara el orden para que sea alfabético, su inteligente ecuación ya no proporcionaría el opuesto correcto.
Josh J