Métodos Java Enum - retorna enum de direção oposta

113

Eu gostaria de declarar um enum Direction, que tem um método que retorna a direção oposta (o seguinte não é sintaticamente correto, ou seja, enums não podem ser instanciados, mas ilustra meu ponto). Isso é possível em Java?

Aqui está o 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
fonte
Basta usar um interruptor. Você só tem 4 casos.
Sotirios Delimanolis
12
A propósito, d.getCode() * -1==-d.getCode()
tckmn
O Capítulo 6 (no 2E, pelo menos) do Java Efetivo de Bloch pode ser interessante e altamente recomendado.
jedwards de
ntu.edu.sg/home/ehchua/programming/java/javaenum.html , a seção 2.1 explicou o conceito de maneira organizada
vikramvi

Respostas:

205

Para aqueles atraídos aqui pelo título: sim, você pode definir seus próprios métodos em seu enum. Se você está se perguntando como invocar esse método não estático, faça-o da mesma forma que com qualquer outro método não estático - invoque-o na instância do tipo que define ou herda esse método. No caso de enums, essas instâncias são simplesmente ENUM_CONSTANTs.

Então, tudo que você precisa é EnumType.ENUM_CONSTANT.methodName(arguments).


Agora vamos voltar ao problema da pergunta. Uma das soluções pode 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;
    }

}

Agora Direction.NORTH.getOppositeDirection()vai voltar Direction.SOUTH.


Aqui está uma forma um pouco mais "hacky" de ilustrar o comentário @jedwards, mas não parece tão flexível quanto a primeira abordagem, pois adicionar mais campos ou alterar sua ordem quebrará nosso 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
fonte
3
Eu estava prestes a preparar um .values()[ordinal()]hack, mas essa abordagem é muito mais robusta
jedwards de
como você usa isso, entretanto? e como é chamada essa técnica?
Thufir
1
@Thufir " como usá-lo " se estiver perguntando sobre o método, então como qualquer outro método - você o invoca na instância da classe com este método. Instâncias de Directionclasse enum são NORTH, EAST, SOUTH, WESTpara que você pode apenas usar NORTH.getOppositeDirection()e ele irá retornar SOUTH. “ como é chamada essa técnica? ” se você está perguntando sobre static{...}então é um bloco de inicialização estático , é um código chamado quando a classe é carregada pela primeira vez (é parte do mesmo processo que inicializa campos estáticos).
Pshemo
@Pshemo, gostaria de saber como será a versão Spring do código acima, se os valores que estão sendo definidos no bloco estático precisam ser injetados a partir, digamos, do arquivo de propriedades.
Vikas Prasad
162

Para um pequeno enum como este, acho que a solução mais legível é:

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
fonte
8
Boa ideia! Isso também é bom quando você geralmente deseja que cada valor de enum tenha um comportamento específico.
OferBr
28

Isso 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
fonte
8
Em vez de retornar null, uma cláusula padrão que lança uma RuntimeException adequada pode ser melhor para indicar que houve um erro do programador em não definir um oposto para uma direção recém-adicionada.
Timothy055
1
Isso requer que o chamador trate de null. Também requer que o mantenedor tenha certeza de adicionar um caso toda vez que um novo tipo de direção for adicionado. Veja a resposta de Amir Afghani sobre o uso de um método abstrato que pode ser substituído por cada valor de enum, dessa forma você nunca corre o risco de perder um e não precisa se preocupar em lidar com nulos.
Michael Peterson
14

Crie um método abstrato e faça com que cada um dos valores de enumeração o substitua. Como você sabe o oposto ao criá-lo, não há necessidade de gerá-lo ou criá-lo dinamicamente.

Embora não tenha uma boa leitura; talvez um switchfosse mais gerenciável?

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

Sim, fazemos isso o tempo todo. Você retorna uma instância estática em vez de um novo 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
fonte
0
public enum Direction {
    NORTH, EAST, SOUTH, WEST;

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

Enums têm um método de valores estáticos que retorna uma matriz contendo todos os valores do enum na ordem em que são declarados. fonte

uma vez que NORTH obtém 1, EAST obtém 2, SOUTH obtém 3, WEST obtém 4; você pode criar uma equação simples para obter o oposto:

(valor + 2)% 4

Pregunton
fonte
2
porque esta é a resposta? Como você espera que isso ajude futuros leitores, ou qualquer pessoa nesse assunto, a aprender sem nenhuma explicação?
GrumpyCrouton
Embora este código possa responder à pergunta, fornecer contexto adicional sobre como e / ou por que ele resolve o problema melhoraria o valor da resposta a longo prazo. Lembre-se de que você está respondendo às perguntas para os leitores no futuro, não apenas para a pessoa que está perguntando agora! Edite sua resposta para adicionar uma explicação e dar uma indicação de quais limitações e suposições se aplicam. Também não faz mal mencionar por que essa resposta é mais apropriada do que outras.
ItamarG3
é difícil ler o código sem comentários? ou você precisa de um javadoc exclusivo para 7 linhas de código?
Pregunton
1
Esta solução é frágil, pois depende da ordem dos valores enum. Se você alterasse a ordem alfabética, sua equação inteligente não forneceria mais o oposto correto.
Josh J