Está tudo bem ir contra o uso de maiúsculas e minúsculas para enumerações para tornar sua representação de String mais simples?


14

Várias vezes eu já vi pessoas usarem maiúsculas ou minúsculas para constantes enum, por exemplo:

enum Color {
  red,
  yellow,
  green;
}

Isso simplifica e facilita o trabalho com a forma de sequência, se você quiser throw new IllegalStateException("Light should not be " + color + "."), por exemplo.

Isso parece mais aceitável se o enum for private, mas ainda não gosto. Eu sei que posso criar um construtor de enum com um campo String e substituir toString para retornar esse nome, assim:

enum Color {
  RED("red"),
  YELLOW("yellow"),
  GREEN("green");

  private final String name;

  private Color(String name) { 
    this.name = name 
  }

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

Mas veja quanto tempo isso é. É chato continuar fazendo se você tem um monte de pequenas enumerações que deseja manter simples. Tudo bem usar apenas formatos de caso não convencionais aqui?


4
É uma convenção. Você tem motivos para quebrar a convenção? Isso é contigo.

9
Basta saber o que há de errado com uma mensagem de exceção que diz Light should not be RED.. Afinal, esta mensagem é para o desenvolvedor, o usuário nunca deve vê-la.
Brandin

1
@Brandin, se a cor é mais um detalhe de implementação, ninguém deve vê-la. Claro, acho que isso deixa a questão de por que precisamos de um bom formulário toString.
Codebreaker

8
No final do dia, é com você. Se eu estava depurando seu código e vi uma mensagem de exceção Light should not be YELLOW., acho que é uma constante de algum tipo, o método toString () é apenas para fins de depuração, portanto não vejo por que você precisa alterar a aparência dessa mensagem.
Brandin

1
Eu acho que a resposta é mais baseada na sua ideia de 'ok'. Provavelmente, o mundo não vai explodir em chamas se você fizer isso e poderá até escrever um programa com sucesso. É útil? meh muito subjetivo. Se você obtiver algum benefício, vale a pena para você e isso afetará os outros? Essas são as perguntas que eu gostaria.
Garet Claborn

Respostas:


14

A resposta curta é, obviamente, se você deseja interromper as convenções de nomenclatura para o que são essencialmente constantes ... Citando o JLS :

Nomes constantes

Os nomes das constantes nos tipos de interface devem ser e as variáveis ​​finais dos tipos de classe convencionalmente podem ser uma sequência de uma ou mais palavras, acrônimos ou abreviações, todas em maiúsculas, com componentes separados por caracteres sublinhados "_". Os nomes constantes devem ser descritivos e não abreviados desnecessariamente. Convencionalmente, eles podem ser qualquer parte apropriada do discurso.

A resposta longa, com relação ao uso de toString(), é definitivamente esse o método a ser substituído, se você quiser uma representação mais legível dos enumvalores. Citando Object.toString()(ênfase minha):

Retorna uma representação de string do objeto. Em geral, o toStringmétodo retorna uma string que "representa textualmente" esse objeto . O resultado deve ser uma representação concisa, mas informativa, fácil para uma pessoa ler . É recomendável que todas as subclasses substituam esse método.

Agora, não sei por que algumas das respostas se voltaram para falar sobre a conversão enumsde um lado para outro com Stringvalores, mas também darei minha opinião aqui. Tal serialização de enumvalores pode ser facilmente tomado cuidado usando o name()ou ordinal()métodos. Ambos são finale, portanto, você pode ter certeza dos valores retornados, desde que os nomes ou o posicionamento dos valores não sejam alterados . Para mim, isso é um marcador suficientemente claro.

O que recordo do exposto é: hoje, você pode querer descrever YELLOWsimplesmente como "amarelo". Amanhã, você pode descrevê-lo como "Pantone Minion Yellow" . Estas descrições devem ser devolvidos a partir chamando toString(), e eu não esperaria tanto name()ou ordinal()à mudança. Se o fizer, é algo que preciso resolver na minha base de código ou na minha equipe e se tornará uma questão maior do que apenas um enumestilo de nomeação .

Concluindo, se tudo o que você pretende fazer é registrar uma representação mais legível dos seus enumvalores, ainda assim sugerirei seguir as convenções e depois substituí-las toString(). Se você também pretende serializá-los em um arquivo de dados ou em outros destinos que não sejam Java, você ainda tem os métodos name()e ordinal()para fazer backup de você, portanto não há necessidade de se preocupar em substituir toString().


5

Não modifique ENUMisso é apenas mau cheiro de código. Deixe um enum ser um enum e uma string ser uma string.

Em vez disso, use um conversor CamelCase para valores de sequência.

throw new IllegalStateException("Light should not be " + CamelCase(color) + ".");

Existem muitas bibliotecas de código aberto que já resolvem esse problema para Java.

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/CaseFormat.html


Não seria não determinístico em certos casos extremos? (não o comportamento de um conversor específico, mas o princípio geral)
Panzercrisis

-1 Adicionar uma biblioteca de terceiros que pode não ser o que você deseja para a formatação básica de String pode ser um exagero.
User949300

1
@ user949300 copie o código, escreva o seu ou o que nunca. Você está perdendo o ponto.
Reactgular

Você pode elaborar "o ponto"? "Deixe um enum ser um enum e uma string ser uma string" soa bem, mas o que isso realmente significa?
User949300 21/04

1
Enum fornece segurança de tipo. Ele não pode passar "potatoe" como uma cor. E, talvez, ele inclua outros dados na enumeração, como o valor thr thr RGB, apenas não os mostra no código. Há muitas boas razões para usar uma enumeração em vez de uma string.
User949300 21/04

3

Ao enviar enumerações entre meu código Java e um banco de dados ou aplicativo cliente, geralmente acabo lendo e gravando os valores de enumeração como seqüências de caracteres. toString()é chamado implicitamente ao concatenar seqüências de caracteres. Substituir toString () em algumas enumerações significava que, às vezes, eu podia

"<input type='checkbox' value='" + MY_CONST1 + "'>"

e às vezes eu tinha que lembrar de ligar

"<input type='checkbox' value='" + MY_CONST1.name() + "'>"

o que levou a erros, então eu não faço mais isso. Na verdade, eu não substituo nenhum método no Enum, porque se você aplicá-lo a um código de cliente suficiente, acabará quebrando as expectativas de alguém.

Faça o seu próprio nome novo método, como public String text()ou toEnglish()ou qualquer outra coisa.

Aqui está uma pequena função auxiliar que pode economizar algumas digitações se você tiver muitas enumerações como as acima:

public static String ucFirstLowerRest(String s) {
    if ( (s == null) || (s.length() < 1) ) {
        return s;
    } else if (s.length() == 1) {
        return s.toUpperCase();
    } else {
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }
}

É sempre fácil chamar .toUpperCase () ou .toLowerCase (), mas voltar a maiúsculas e minúsculas pode ser complicado. Considere a cor "bleu de France". A França está sempre em maiúscula, portanto, você pode adicionar um método textLower () à sua enumeração, se você o encontrar. Quando você usa esse texto no início de uma frase, no meio de uma frase e em um título, pode ver como um único toString()método ficará aquém. E isso nem toca em caracteres que são ilegais nos identificadores Java, ou que são difíceis de digitar porque não são representados em teclados padrão ou caracteres que não possuem maiúsculas (Kanji, etc.).

enum Color {
  BLEU_DE_FRANCE {
    @Override public String textTc() { return "Bleu De France"; }
    @Override public String textLc() { return "bleu de France"; }
  }
  CAFE_NOIR {
    @Override public String textTc() { return "Café Noir"; }
  }
  RED,
  YELLOW,
  GREEN;

  // The text in title case
  private final String textTc;

  private Color() { 
    textTc = ucFirstLowerRest(this.toString());
  }

  // Title case
  public String textTc() { return textTc; }

  // For the middle of a sentence
  public String textLc() { return textTc().toLowerCase(); }

  // For the start of a sentence
  public String textUcFirst() {
    String lc = textLc();
    return lc.substring(0, 1).toUpperCase() + lc.substring(1);
  }
}

Não é tão difícil usá-los corretamente:

IllegalStateException(color1.textUcFirst() + " clashes horribly with " +
                      color2.textLc() + "!")

Espero que isso também demonstre por que o uso de valores de enumeração de maiúsculas e minúsculas também o decepcionará. Um último motivo para manter as letras maiúsculas com sublinhados constantes enum é que isso segue o Princípio da menor surpresa. As pessoas esperam isso; portanto, se você fizer algo diferente, sempre terá que se explicar ou lidar com pessoas que abusam do seu código.


3
A sugestão de nunca substituir toString()um enum não faz sentido para mim. Se você deseja o comportamento padrão garantido dos nomes de enum, use name(). Não acho "tentador" confiar em tudo toString()para fornecer a chave correta valueOf(String). Eu acho que se você quiser proteger seus enums com idiotas, isso é uma coisa, mas não acho que seja uma razão boa o suficiente para recomendar que você nunca deve substituir toString().
Codebreaker 20/05

1
Além disso, eu encontrei este, que recomenda (como fui levado a acreditar) que substituir toString sobre enums é de fato uma coisa boa: stackoverflow.com/questions/13291076/...
codebreaker

@codebreaker toString () é chamado implicitamente ao concatenar strings. Substituir toString () em algumas enumerações significava que, às vezes, eu podia apenas <input type='checkbox' value=" + MY_CONST1 + ">, e às vezes tinha que me lembrar de ligar <input type='checkbox' value=" + MY_CONST1.name() + ">, o que levava a erros.
precisa saber é o seguinte

Ok, esse é um bom argumento, mas só importa quando você está "serializando" enumerações com o nome delas (que não é com isso que eu preciso me preocupar com o meu exemplo).
Codebreaker 21/05

0

Se houver a menor chance de que essas representações de seqüência de caracteres possam ser armazenadas em locais como bancos de dados ou arquivos de texto, vinculá-las às constantes reais da enumeração se tornará altamente problemática se você precisar refatorar sua enumeração.

E se um dia você decidir mudar o nome Color.Whitepara Color.TitaniumWhitequando houver arquivos de dados lá fora, que contêm "White"?

Além disso, quando você começa a converter constantes enum em strings, o próximo passo adiante é gerar strings para o usuário ver, e o problema aqui é, como eu já sei, que a sintaxe do java não permitir espaços dentro dos identificadores. (Você nunca terá Color.Titanium White.) Portanto, como você provavelmente precisará de um mecanismo apropriado para gerar nomes de enumeração para mostrar ao usuário, é melhor evitar complicar desnecessariamente sua enumeração.

Dito isto, é claro que você pode seguir em frente e fazer o que estava pensando em fazer, e depois refatorar sua enumeração mais tarde, para passar nomes ao construtor, quando (e se) tiver problemas.

Você pode raspar algumas linhas do seu enum-com-construtor declarando o namecampo public finale perdendo o getter. (Que amor é esse que os programadores java têm em relação aos getters?)


Uma estática final pública é compilada no código que a usa como a constante real, e não como uma referência à classe da qual ela é originada. Isso pode causar vários outros erros divertidos ao recompilar parcialmente o código (ou substituir um jar). Se você está sugerindo public final static int white = 1;ou public final static String white = "white";(além da quebra da convenção novamente), isso perde o tipo de segurança que a enum fornece.

Os campos @MichaelT Enum não são estáticos. Uma instância é criada para cada constante de enumeração e os membros da enumeração são variáveis ​​de membro dessa instância. Um public finalcampo de instância que é inicializado a partir do construtor se comporta exatamente como um campo não final, exceto que você é impedido em tempo de compilação de atribuir a ele. Você pode modificá-lo via reflexão e todo o código que se refere a ele começará imediatamente a ver o novo valor.
Mike Nakis

0

Indiscutivelmente, seguir a convenção de nomenclatura UPPERCASE viola DRY . (E YAGNI ). por exemplo, no exemplo de código 2: "RED" e "red" são repetidos.

Então, você segue a convenção oficial ou segue DRY? Sua chamada.

No meu código, seguirei DRY. No entanto, colocarei um comentário na classe Enum, dizendo "nem todas maiúsculas porque (explicação aqui)"

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.