Substituir valueof () e toString () na enumeração Java


122

Os valores em meu enumsão palavras que precisam ter espaços neles, mas enumerações não podem ter espaços em seus valores; portanto, tudo está agrupado. Eu quero substituir toString()para adicionar esses espaços onde eu digo.

Também quero que a enumeração forneça a enumeração correta quando uso valueOf()a mesma sequência em que adicionei os espaços.

Por exemplo:

public enum RandomEnum
{
     StartHere,
     StopHere
}

Chamada toString()em RandomEnumcujo valor é StartHereseqüência de retornos "Start Here". A chamada valueof()nessa mesma string ( "Start Here") retorna um valor de enumeração StartHere.

Como posso fazer isso?


2
Adicione a tag "Java" para obter mais comentários / respostas.
— Java42

Respostas:


197

Você pode experimentar este código. Como você não pode substituir o valueOfmétodo, é necessário definir um método personalizado ( getEnumno código de exemplo abaixo) que retorne o valor necessário e altere seu cliente para usar esse método.

public enum RandomEnum {

    StartHere("Start Here"),
    StopHere("Stop Here");

    private String value;

    RandomEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return this.getValue();
    }

    public static RandomEnum getEnum(String value) {
        for(RandomEnum v : values())
            if(v.getValue().equalsIgnoreCase(value)) return v;
        throw new IllegalArgumentException();
    }
}

4
getEnum pode ser reduzido se você fizer o seguinte: v.getValue (). equals (value); a verificação nula pode ser omitida.
— Rtcarlson 27/03

Você também pode construir preguiçosamente um mapa e reutilizá-lo, mas cuidado com os problemas de segmentação enquanto o constrói.
— Maarten Bodewes

11
não trabalho dentro de cenários onde você não pode mudar a vocação de valueOf () para getValue () por exemplo, quando você definir determinados campos através de anotações do Hibernate para mapear valores enum
— mmierins

Até que as enums sejam corrigidas em Java, o @Bat tem uma solução mais apropriada e amigável para OO. name () não deve ser final.
— Andrew T Finnell

em vez de para você pode usar valueOf (RandomEnum.class, value);
— Wojtek

22

Tente isso, mas não sei se isso funcionará em todos os lugares :)

public enum MyEnum {
    A("Start There"),
    B("Start Here");

    MyEnum(String name) {
        try {
            Field fieldName = getClass().getSuperclass().getDeclaredField("name");
            fieldName.setAccessible(true);
            fieldName.set(this, name);
            fieldName.setAccessible(false);
        } catch (Exception e) {}
    }
}

18
Por que eu sou o único a quem esse tipo de coisa parece ruim? Quero dizer, parece muito legal, mas alguém sem contexto que lê esse código provavelmente seria como "WTF?".
— Nicolas Guillaume

11
@ NicolasGuillaume Esse é o tipo de código que, se implementado, precisaria desesperadamente de um comentário explicando por que está lá e que problema o programador estava tentando resolver. :-)
— Ti Strga 06/06

9
Não pegar exceção genérica, como isso pode ser OutOfMemory ou qualquer coisa
— crusy

2
Esta é uma péssima ideia. Não menos importante é a possibilidade de erro silencioso sem indicação ou recuperação. Agora também o valor "nome" e o valor simbólico no código (por exemplo, A, B) são diferentes. +2 por ser inteligente. -200 por ser terrivelmente inteligente. Não há como eu aprovar isso como uma revisão de código.
— pedorro

1
@pedorro Isso não parece tão terrível quanto você está imaginando. Qualquer um que entenda que tudo o que o comitê Java faz não deve ser considerado como Evangelho passaria isso em uma Revisão de Código. É infinitamente pior ter que reescrever e manter todos os métodos do utilitário Enum porque o nome () não pode ser substituído. Em vez de capturar a exceção e engoli-la, uma RuntimeException deve ser lançada e isso é bom.
— Andrew T Finnell

14

Você pode usar um mapa estático em sua enumeração que mapeia Strings para constantes de enumeração. Use-o em um método estático 'getEnum'. Isso ignora a necessidade de iterar pelas enumerações sempre que você deseja obter uma do valor String.

public enum RandomEnum {

    StartHere("Start Here"),
    StopHere("Stop Here");

    private final String strVal;
    private RandomEnum(String strVal) {
        this.strVal = strVal;
    }

    public static RandomEnum getEnum(String strVal) {
        if(!strValMap.containsKey(strVal)) {
            throw new IllegalArgumentException("Unknown String Value: " + strVal);
        }
        return strValMap.get(strVal);
    }

    private static final Map<String, RandomEnum> strValMap;
    static {
        final Map<String, RandomEnum> tmpMap = Maps.newHashMap();
        for(final RandomEnum en : RandomEnum.values()) {
            tmpMap.put(en.strVal, en);
        }
        strValMap = ImmutableMap.copyOf(tmpMap);
    }

    @Override
    public String toString() {
        return strVal;
    }
}

Apenas verifique se a inicialização estática do mapa ocorre abaixo da declaração das constantes enum.

BTW - esse tipo 'ImmutableMap' é da API do Google goiaba, e eu definitivamente recomendo em casos como este.


EDIT - Pelos comentários:

  1. Esta solução pressupõe que cada valor de sequência atribuído seja exclusivo e não nulo. Dado que o criador do enum pode controlar isso, e que a string corresponde ao valor de enum exclusivo e não nulo, isso parece uma restrição segura.
  2. Adicionei o método 'toSTring ()' conforme solicitado na pergunta

1
Recebi alguns votos negativos nesta resposta - alguém se preocupa em explicar por que essa resposta é ruim? Só estou curioso no que as pessoas estão pensando.
— Pedorro

Isso falhará no cenário com várias constantes enum com o mesmo 'valor' igual RestartHere("replay"), ReplayHere("replay")ou nenhum 'valor' igual PauseHere, WaitHere(ou seja, o enum tem um construtor padrão semelhante aprivate RandomEnum() { this.strVal = null; }
— sactiw

1
Você deve usar ImmutableMap.Builder em vez de criar um MutableMap para criar + ImmutableMap :: copyOf.
— Bryan Correll

Isso parece interessante, mas se o enum tiver apenas alguns valores diferentes, não demorará muito para iterar sobre eles. Provavelmente só vale a pena se o enum tiver muitos valores.
— Ben Thurley

13

Que tal uma implementação do Java 8? (null pode ser substituído pelo seu Enum padrão)

public static RandomEnum getEnum(String value) {
    List<RandomEnum> list = Arrays.asList(RandomEnum.values());
    return list.stream().filter(m -> m.value.equals(value)).findAny().orElse(null);
}

Ou você pode usar:

...findAny().orElseThrow(NotFoundException::new);

2

Eu não acho que você vai obter valor de ("Start Here") para funcionar. Mas quanto aos espaços ... tente o seguinte ...

static private enum RandomEnum {
    R("Start There"), 
    G("Start Here"); 
    String value;
    RandomEnum(String s) {
        value = s;
    }
}

System.out.println(RandomEnum.G.value);
System.out.println(RandomEnum.valueOf("G").value);

Start Here
Start Here

2

A seguir, é uma boa alternativa genérica para valueOf ()

public static RandomEnum getEnum(String value) {
  for (RandomEnum re : RandomEnum.values()) {
    if (re.description.compareTo(value) == 0) {
      return re;
    }
  }
  throw new IllegalArgumentException("Invalid RandomEnum value: " + value);
}

o uso é compareTo()mais elegante do que equals()para Strings?
— Betlista 27/01

Elegância não é um problema - concordo que .equals()funcionaria bem. Poderia usar ==se você gosta. (Embora uma boa razão para não fazer isso seja, você deve substituir o enum por uma classe.)
— exceção

3
@exception NUNCA USE == para comparações de String, pois ele fará a verificação de igualdade de objetos enquanto você precisa da verificação de igualdade de conteúdo e, para isso, use o método equals () da classe String.
— sactiw

2

Você ainda tem uma opção para implementar em sua enumeração isso:

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name){...}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.