Enums de Java são ótimas. O mesmo acontece com os genéricos. É claro que todos sabemos as limitações deste último por causa do apagamento de tipo. Mas há uma coisa que não entendo: por que não consigo criar uma enumeração como esta:
public enum MyEnum<T> {
LITERAL1<String>,
LITERAL2<Integer>,
LITERAL3<Object>;
}
Esse parâmetro de tipo genérico, <T>
por sua vez, pode ser útil em vários locais. Imagine um parâmetro de tipo genérico para um método:
public <T> T getValue(MyEnum<T> param);
Ou mesmo na própria classe enum:
public T convert(Object o);
Exemplo mais concreto nº 1
Como o exemplo acima pode parecer abstrato demais para alguns, aqui está um exemplo mais real do porquê eu quero fazer isso. Neste exemplo eu quero usar
- Enums, porque então eu posso enumerar um conjunto finito de chaves de propriedade
- Genéricos, porque então eu posso ter segurança de tipo no nível do método para armazenar propriedades
public interface MyProperties {
public <T> void put(MyEnum<T> key, T value);
public <T> T get(MyEnum<T> key);
}
Exemplo mais concreto # 2
Eu tenho uma enumeração de tipos de dados:
public interface DataType<T> {}
public enum SQLDataType<T> implements DataType<T> {
TINYINT<Byte>,
SMALLINT<Short>,
INT<Integer>,
BIGINT<Long>,
CLOB<String>,
VARCHAR<String>,
...
}
Cada literal de enumeração obviamente teria propriedades adicionais baseadas no tipo genérico <T>
, enquanto ao mesmo tempo seria uma enumeração (imutável, singleton, enumerável, etc. etc.)
Questão:
Ninguém pensou nisso? Isso é uma limitação relacionada ao compilador? Considerando o fato de que a palavra-chave " enum " é implementada como açúcar sintático, representando o código gerado para a JVM, não entendo essa limitação.
Quem pode me explicar isso? Antes de responder, considere o seguinte:
- Eu sei que tipos genéricos são apagados :-)
- Eu sei que existem soluções alternativas usando objetos de classe. Eles são soluções alternativas.
- Tipos genéricos resultam em transmissões de tipos gerados pelo compilador sempre que aplicável (por exemplo, ao chamar o método convert ()
- O tipo genérico <T> estaria na enumeração. Portanto, ele é vinculado por cada um dos literais da enumeração. Portanto, o compilador saberia qual tipo aplicar ao escrever algo como
String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
- O mesmo se aplica ao parâmetro de tipo genérico no
T getvalue()
método O compilador pode aplicar a conversão de tipo ao chamarString string = someClass.getValue(LITERAL1)
enum
idioma em "typesafe enum" que usamos antes do Java 1.5. De repente, você pode ter seus membros da enum parametrizados. Provavelmente é o que vou fazer agora.Respostas:
Agora isso está sendo discutido no JEP-301 Enhanced Enums . O exemplo dado no JEP é exatamente o que eu estava procurando:
Infelizmente, o JEP ainda está enfrentando problemas significativos: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html
fonte
A resposta está na pergunta:
Nenhum desses dois métodos é possível, pois o tipo de argumento é apagado.
Para realizar esses métodos, você pode construir sua enumeração como:
fonte
public T convert(Object);
. Eu estou supondo que este método poderia, por exemplo, restringir um monte de tipos diferentes para <T>, que por exemplo é uma String. A construção do objeto String é em tempo de execução - nada que o compilador faça. Portanto, você precisa conhecer o tipo de tempo de execução, como String.class. Ou eu estou esquecendo de alguma coisa?public final static String FOO;
com um bloco estáticostatic { FOO = "bar"; }
- também as constantes são avaliadas mesmo quando conhecidas em tempo de compilação.Porque você não pode. Seriamente. Isso pode ser adicionado à especificação do idioma. Não foi. Isso acrescentaria alguma complexidade. Esse benefício de custo significa que não é uma alta prioridade.
Atualização: atualmente sendo adicionada ao idioma no JEP 301: Enums aprimoradas .
fonte
Existem outros métodos no ENUM que não funcionariam. O que seria
MyEnum.values()
retornaria?A respeito
MyEnum.valueOf(String name)
?Para o valueOf, se você acha que o compilador pode criar um método genérico como
para chamar assim
MyEnum<String> myStringEnum = MyEnum.value("some string property")
, isso também não funcionaria. Por exemplo, e se você ligarMyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")
? Não é possível implementar esse método para funcionar corretamente, por exemplo, para lançar uma exceção ou retornar nulo quando você o chama comoMyEnum.<Int>value("some double property")
por causa do tipo apagamento.fonte
MyEnum<?>[] values()
eMyEnum<?> valueOf(...)
MyEnum<Int> blabla = valueOf("some double property");
porque os tipos não são compatíveis. Além disso, nesse caso, você deseja obter nulo porque deseja retornar MyEnum <Int> que não existe para o nome da propriedade dupla e não é possível fazer com que esse método funcione corretamente devido ao apagamento.Enum
tem um monte de outras funcionalidades úteis, e este seria opcional e muito útil, bem ...Francamente, isso parece mais uma solução em busca de um problema do que qualquer coisa.
O objetivo inteiro do java enum é modelar uma enumeração de instâncias de tipo que compartilhem propriedades semelhantes de uma maneira que forneça consistência e riqueza além das representações String ou Inteiro comparáveis.
Veja um exemplo de enum de livro de texto. Isso não é muito útil ou consistente:
Por que eu gostaria que meus planetas diferentes tivessem conversões de tipos genéricos diferentes? Qual problema isso resolve? Justifica complicar a semântica da linguagem? Se eu precisar desse comportamento, um enum é a melhor ferramenta para alcançá-lo?
Além disso, como você gerenciaria conversões complexas?
por exemplo
É fácil o suficiente
String
Integer
para fornecer o nome ou ordinal. Mas os genéricos permitiriam fornecer qualquer tipo. Como você gerenciaria a conversãoMyComplexClass
? Agora, você está criando duas construções forçando o compilador a saber que há um subconjunto limitado de tipos que podem ser fornecidos a enumerações genéricas e introduzindo uma confusão adicional ao conceito (Genéricos) que já parece iludir muitos programadores.fonte
interface Converter<IN, OUT> { OUT convert(IN in); } <E> Set<E> convertListToSet(List<E> in, Converter<? super List<E>, ? extends Set<E>> converter) { return converter.convert(in); }
Temos que trabalhar manualmente cada vez que tipo é consumido e qual produzido e especificar os limites de acordo.Porque "enum" é a abreviação de enumeração. É apenas um conjunto de constantes nomeadas que substituem os números ordinais para tornar o código melhor legível.
Não vejo qual poderia ser o significado pretendido de uma constante parametrizada por tipo.
fonte
java.lang.String
:public static final Comparator<String> CASE_INSENSITIVE_ORDER
. Você vê agora? :-)CASE_INSENSITIVE_ORDER
... SeComparator<T>
havia um enum, por que não ter literais com qualquer associado associado<T>
?Eu acho que porque basicamente Enums não podem ser instanciados
Onde você definiria a classe T, se a JVM permitisse?
Enumeração são dados que devem ser sempre os mesmos, ou pelo menos, que não serão alterados dinamicamente.
novo MyEnum <> ()?
Ainda a seguinte abordagem pode ser útil
fonte
setO()
método em umenum
. Considero constantes enum e para mim isso implica imutável . Então, mesmo que seja possível, eu não faria isso.