As Enums do Java ganharão suporte ao generics com a possibilidade de adicionar métodos para itens individuais. Uma vez que ambas funcionalidades podem ser entregues com a mesma mudança de código, foram empacotadas na mesma JEP. A mudança afeta somente o compilador do Java, portanto não é necessário alterar o runtime. Embora não tenha sido definida uma versão, provavelmente chegará no Java 10.
A mudança inicialmente não teve uma reação positiva, com Java Champions de renome como Joshua Bloch questionando sua utilidade. Entretanto, alguns debates mais aprofundados e a apresentação de novos casos de uso ajudaram a obter apoio.
In case you didn't see my resp to @BrianGoetz, I now see the point of the JEP and withdraw my objections. Use case:https://t.co/O1tJO8oSCp
— Joshua Bloch (@joshbloch) December 7, 2016
Veremos o que o desenvolvedor pode fazer resumindo alguns dos casos de usos apresentados no JEP e outras discussões. O Lukas Eder, Java Champion, postou uma pergunta no StackOverflow sobre um caso para recuperar e definir propriedades a partir de um arquivo de configuração, sessão web ou similar de uma maneira segura. Um enum com suporte à generics permitiria indicar os conjuntos de chaves disponíveis junto com o seu tipo associado:
public enum Key {
HOST,
PORT,
SCORE
}
public interface PropertiesStore {
public void put(Key key, T value);
public T get(Key key);
}
Agora estas chaves podem ser recuperadas e salvas com segurança, uma vez que expressões como a seguir falharão em tempo de compilação:
put(PORT, “not a number”); // Erro, tipo incompatível: PORT é Key<Integer>
// "not a number" é String
Em contrapartida, permitir que itens possam ter seus próprios métodos pode ajudar a definir operações que são aplicadas somente para certas propriedades. Sob a JEP 301, a definição anterior pode ser expandida da seguinte maneira:
public enum Key {
HOST,
PORT,
SCORE {
double normalise(double x) {
// lógica de normalização de score
return result;
}
}
}
Atualmente todos os itens do enum teriam o tipo genérico Key, significando que o método normalise não seria visível. Entretanto, após a conclusão da alteração, o compilador preservaria este tipo de informação, desta forma o seguinte seria verdade:
SCORE.normalise(5.37); // compila
HOST.normalise(5.37); // erro: ambos HOST e Key nâo tem o normalise
A maneira para conseguir isso será alterando a forma como são expostos os tipos estáticos dos itens individuais do enum. As enums foram adicionadas apenas para facilitar a vida: a JVM não tem nenhum tratamento especial para enums, o compilador traduz o código de uma enum para uma classe normal com objetos estáticos, e então a compila em bytecode. Deixando de lado alguns aspectos técnicos, segue uma enum:
public enum Colour {
RED, GREEN, BLUE
}
O compilador traduz para algo similar a representação a seguir:
public class Colour extends Enum {
public static final Colour RED = new Colour();
public static final Colour GREEN = new Colour();
public static final Colour BLUE = new Colour();
private Colour() {}
}
Dado que todos os itens são do tipo Colour, qualquer método específico ou informação de tipo é perdida. O que a JEP 301 propõe é identificar os casos no qual o uso de um enum de tipo genérico não é o bastante para representar os itens individualmente, e neste caso gerar um tipo mais específico como Colour$RED, Colour$GREEN e Colour$BLUE.
As enums melhoradas podem se beneficiar ainda mais de outros esforços que estão sendo realizados atualmente em outras partes do JDK. Por um lado, a inferência de tipos em variáveis locais pode permitir que os desenvolvedores aproveitem dos tipos criados, mesmo que seja desconhecido no momento da codificação, isso significa que, considerando o código anterior, o seguinte seria possível:
var s = Key.SCORE; // tipo de s derivado como Key$SCORE
s.normalise(8.29); // // o método normalise pode ser acessado
Entretanto, analisando as evidências e casos de uso, os exemplos sugerem que generics em enums podem ser usados extensivamente com tipos primitivos, o que é bastante ineficiente (já que a JVM tem que usar o autoboxing para cada primitivo). Isso pode ser aperfeiçoado com parte do projeto Valhalla que lida com Generics sobre tipos primitivos, removendo essa barreira e disponibilizando para o uso em massa.