Um motivo para fazer isso é testar a situação em que há um valor de enum inválido sem introduzir um valor de enum inválido na fonte principal.
Archimedes Trajano
Sim, um exemplo de pureza "linguística". Eu acho que o que se deseja é a ideia de economia de mão-de-obra "bookeeping" de um conjunto de números inteiros com incremento automático, como um em C ++, para que você possa iniciar um novo conjunto como uma extensão do conjunto antigo, começando com 1+ o último valor do conjunto anterior e, se as entradas forem nomeadas, herdam os nomes do "subconjunto comum". Embora o java enum tenha algumas coisas interessantes, ele não possui a ajuda simples e simples de declaração automática de incremento automático automatizada que o C ++ enum fornece.
peterk
4
Na verdade, quando você estende sua enumeração com novos valores, está criando não uma subclasse, mas uma superclasse. Você pode usar valores de enum base em todos os lugares, em vez de enum "estendido", mas não vice-versa; portanto, de acordo com o Princípio de Substituição de Liskov, o enum estendido é uma superclasse de enum base.
Ilya
@ Ilya ... sim, isso é verdade. Ressalto que a questão tem casos de uso definidos no mundo real. Por causa do argumento, considere uma base de Enum de: PrimaryColours; é razoável querer Super de classe que isso Enum PrimaryAndPastelColourspela adição de novos nomes de cores. Liskov ainda é o elefante na sala. Então, por que não começar com um Enum base de: AllMyColours- E então pode-se subclassificar todas as cores para: PrimaryAndPastelColourse subclasses subseqüentemente isso para: PrimaryColours(mantendo a hierarquia em mente). O Java também não permitirá isso.
será
Respostas:
451
Não, você não pode fazer isso em Java. Além de qualquer outra coisa, dpresumivelmente seria uma instância de A(dada a idéia normal de "estende"), mas usuários que apenas conheciamA não sabiam disso - o que derrota o ponto de um enum ser um conjunto bem conhecido de valores.
Se você puder nos dizer mais sobre como deseja usar isso, poderíamos sugerir soluções alternativas.
Todas as enumerações estendem implicitamente java.lang.Enum. Como o Java não suporta herança múltipla, uma enum não pode estender mais nada.
givanse
9
A razão pela qual desejo estender é porque gostaria de ter uma classe base chamada, por exemplo, IntEnum, que se parece com isso: stackoverflow.com/questions/1681976/enum-with-int-value-in-java/… . Então todas as minhas enumerações poderiam estendê-la ... nesse caso, apenas se beneficiando da herança e, portanto, não precisaria duplicar esse código de "enumeração baseada em int" com freqüência. Eu sou novo em Java e sou de C #, e espero estar perdendo alguma coisa. Minha opinião atual é que enums Java são uma dor em comparação com C #.
Tyler Collier
30
@ Tyler: enums de C # são apenas nomes associados a números, sem validação automática ou algo assim . As enums da IMO são o bit de Java que é realmente melhor que o C #.
precisa
21
Não estou de acordo com @JonSkeet aqui. No meu caso de uso, gostaria de separar toda a lógica desagradável no meu grande enum, ocultar a lógica e definir uma enum limpa que estenda a outra oculta. Enums com muita lógica superam a idéia de declarar variáveis limpas, assim você não precisa declarar centenas de variáveis de strings estáticas para que uma classe com 5 enums não fique ilegível e muito grande nas linhas. Também não quero que os outros desenvolvedores se preocupem em copiar e colar essa paz de código para o próximo projeto e, em vez disso, estender o base_enum ... faz sentido para mim ...
mmm
43
@givanse ... não concordando com você no ponto de extensão implícita do java.lang.Enum, sendo a causa da não-herança, pois todas as classes em java também herdam implicitamente a classe Object, mas podem herdar alguma outra classe, pois ela viria na hierarquia como Object->A->Bem vez deObject->A->B extends Object
mickeymoon
317
Enums representam uma enumeração completa de valores possíveis. Portanto, a resposta (inútil) é não.
Como exemplo de um problema real, considere os dias da semana, finais de semana e, a união, os dias da semana. Poderíamos definir todos os dias dentro de dias da semana, mas não poderíamos representar propriedades especiais para dias da semana e fins de semana.
O que poderíamos fazer é ter três tipos de enumeração com um mapeamento entre dias da semana / dias da semana e dias da semana.
Não há um problema com isso? Uma instrução switch não funciona em uma interface, mas funciona em uma enumeração regular. Não funcionar com o tipo de switch mata uma das coisas mais agradáveis sobre enums.
Manius 29/07
9
Estou pensando que pode haver outro problema com isso. Não há igualdade entre o Weekday.MON e DayOfWeek.MON. Não é esse o outro grande benefício das enums? Não tenho uma solução melhor, apenas percebi isso enquanto tentava encontrar a melhor resposta. A falta de poder usar == força um pouco a mão.
Snekse
2
@ Crusader sim, essa é precisamente a troca. Se você deseja algo expansível, não pode ter instruções de chave fixas, se deseja um conjunto de valores conhecidos fixos, tautologicamente não pode ter algo expansível.
djechlin
3
Indo de enum para interface, você também perde a chamada estática para values (). Isso dificulta a refatoração, especialmente se você decidir estender sua enumeração e adicionar a interface como uma barreira de abstração a uma enumeração estabelecida.
Joshua Goldberg
4
Essa abordagem de derivar uma enumeração de uma interface é usada pela API Java 1.7, por exemplo, java.nio.file.Files.write () pega uma matriz de OpenOption como o último argumento. OpenOption é uma interface, mas quando chamamos essa função geralmente passamos uma constante de enum StandardOpenOption, que é derivada de OpenOption. Isso tem a vantagem de ser extensível, mas também possui desvantagens. A implementação sofre com o fato de o OpenOption ser uma interface. Ele cria um HashSet <OpenOption> a partir da matriz passada, quando poderia ter criado um EnumSet com mais espaço e tempo. E não pode usar o interruptor.
Isso envolve criar uma interface e usá-la onde você usa a enumeração. Em seguida, faça o enum implementar a interface. Você pode adicionar mais constantes, fazendo com que a nova enum também estenda a interface.
Vale a pena destacar o uso de um método de fábrica na interface. Ótima maneira de compartilhar funcionalidades comuns entre as Enums relacionadas, pois a extensão não é uma solução viável.
Tim Clemons
8
Você pode fornecer mais detalhes (código :)) sobre esse padrão?
Dherik
3
Esse padrão não permite estender os valores de uma enumeração. Qual é o ponto da pergunta.
Eria
55
Nos bastidores, seu ENUM é apenas uma classe regular gerada pelo compilador. Essa classe gerada se estende java.lang.Enum. A razão técnica pela qual você não pode estender a classe gerada é que a classe gerada éfinal . As razões conceituais para a sua finalização são discutidas neste tópico. Mas vou adicionar a mecânica à discussão.
Aqui está uma enumeração de teste:
publicenum TEST {
ONE, TWO, THREE;}
O código resultante do javap:
publicfinalclass TEST extends java.lang.Enum<TEST>{publicstaticfinal TEST ONE;publicstaticfinal TEST TWO;publicstaticfinal TEST THREE;static{};publicstatic TEST[] values();publicstatic TEST valueOf(java.lang.String);}
É possível que você possa digitar essa classe por conta própria e soltar a "final". Mas o compilador impede que você estenda "java.lang.Enum" diretamente. Você pode decidir NÃO estender o java.lang.Enum, mas sua classe e suas classes derivadas não seriam uma instância do java.lang.Enum ... o que pode não ser realmente importante para você!
O que o bloco estático vazio está fazendo? 'static {};'
soote
1
Não possui código. O programa "javap" mostra o bloco vazio.
22416 ChrisCantrell
Estranho tê-lo lá, se não estiver fazendo nada, não é?
soote
4
Você está certo! Meu erro. NÃO é um bloco de código vazio. Se você executar "javap -c", verá o código real dentro do bloco estático. O bloco estático cria todas as instâncias ENUM (uma, duas e três aqui). Me desculpe por isso.
22416 ChrisCantrell
1
Obrigado por declarar o fato direto: porque java.lang.Enum é declarado final.
Benjamin
26
enum A {a,b,c}enum B extends A {d}/*B is {a,b,c,d}*/
pode ser escrito como:
publicenumAll{
a (ClassGroup.A,ClassGroup.B),
b (ClassGroup.A,ClassGroup.B),
c (ClassGroup.A,ClassGroup.B),
d (ClassGroup.B)...
ClassGroup.B.getMembers () contém {a, b, c, d}
Como pode ser útil: Digamos que queremos algo como: Temos eventos e estamos usando enumerações. Essas enumerações podem ser agrupadas por processamento semelhante. Se temos operação com muitos elementos, alguns eventos iniciam a operação, alguns são apenas um passo e outro finalizam a operação. Para reunir essa operação e evitar casos de comutação longos, podemos agrupá-los como no exemplo e usar:
Acima, se tivermos alguma falha (myEvent.is (State_StatusGroup.FAIL)), iterando pelos eventos anteriores, podemos verificar facilmente se devemos reverter a transferência de dinheiro por:
interessante, poderíamos usar também o enum toString () e, no final, comparar strings; e para usar switch, teríamos apenas que converter o objeto em uma enumeração conhecida; o único problema seria 2 desenvolvedores estendendo e criando um id de enum idêntico e depois tentando mesclar os dois códigos :), agora acho que entendo por que o enum deve permanecer não extensível.
Uma pequena desvantagem do uso de interfaces para emular enums extensíveis é que as implementações não podem ser herdadas de um tipo de enumeração para outro. No caso do nosso exemplo de Operação, a lógica para armazenar e recuperar o símbolo associado a uma operação é duplicada em BasicOperation e ExtendedOperation. Nesse caso, não importa, porque muito pouco código é duplicado. Se houvesse uma quantidade maior de funcionalidade compartilhada, você poderia encapsulá-la em uma classe auxiliar ou em um método auxiliar estático para eliminar a duplicação de código.
Em resumo, embora você não possa escrever um tipo de enum extensível, você pode emulá-lo escrevendo uma interface para usar um tipo de enum básico que implementa a interface. Isso permite que os clientes escrevam suas próprias enumerações que implementam a interface. Essas enumerações podem ser usadas sempre que o tipo de enumeração básico puder ser usado, assumindo que as APIs sejam gravadas em termos da interface.
Eu tendem a evitar enums, porque eles não são extensíveis. Para ficar com o exemplo do OP, se A estiver em uma biblioteca e B em seu próprio código, você não poderá estender A se for um enum. É assim que às vezes substituo enums:
// access like enum: A.apublicclass A {publicstaticfinal A a =new A();publicstaticfinal A b =new A();publicstaticfinal A c =new A();/*
* In case you need to identify your constant
* in different JVMs, you need an id. This is the case if
* your object is transfered between
* different JVM instances (eg. save/load, or network).
* Also, switch statements don't work with
* Objects, but work with int.
*/publicstaticint maxId=0;publicint id = maxId++;publicint getId(){return id;}}publicclass B extends A {/*
* good: you can do like
* A x = getYourEnumFromSomeWhere();
* if(x instanceof B) ...;
* to identify which enum x
* is of.
*/publicstaticfinal A d =new A();}publicclass C extends A {/* Good: e.getId() != d.getId()
* Bad: in different JVMs, C and B
* might be initialized in different order,
* resulting in different IDs.
* Workaround: use a fixed int, or hash code.
*/publicstaticfinal A e =new A();publicint getId(){return-32489132;};}
Existem alguns poços a serem evitados, veja os comentários no código. Dependendo das suas necessidades, essa é uma alternativa sólida e extensível às enums.
pode ser bom se você precisar apenas de um ordinal para instâncias. Mas enums também possuem uma propriedade de nome que é bastante útil.
inor
6
É assim que eu aprimoro o padrão de herança de enum com verificação de tempo de execução no inicializador estático. As BaseKind#checkEnumExtenderverificações que "estendendo" a enum declaram todos os valores da enum base exatamente da mesma maneira, #name()e#ordinal() permanecem totalmente compatíveis.
Ainda há copiar-colar envolvido para declarar valores, mas o programa falha rapidamente se alguém adiciona ou modifica um valor na classe base sem atualizar os estendidos.
Comportamento comum para enums diferentes que se estendem:
publicinterfaceKind{/**
* Let's say we want some additional member.
*/String description();/**
* Standard {@code Enum} method.
*/String name();/**
* Standard {@code Enum} method.
*/int ordinal();}
Enum base, com método de verificação:
publicenumBaseKindimplementsKind{
FIRST("First"),
SECOND("Second"),;privatefinalString description ;publicString description(){return description ;}privateBaseKind(finalString description ){this.description = description ;}publicstaticvoid checkEnumExtender(finalKind[] baseValues,finalKind[] extendingValues
){if( extendingValues.length < baseValues.length ){thrownewIncorrectExtensionError("Only "+ extendingValues.length +" values against "+ baseValues.length +" base values");}for(int i =0; i < baseValues.length ; i ++){finalKind baseValue = baseValues[ i ];finalKind extendingValue = extendingValues[ i ];if( baseValue.ordinal()!= extendingValue.ordinal()){thrownewIncorrectExtensionError("Base ordinal "+ baseValue.ordinal()+" doesn't match with "+ extendingValue.ordinal());}if(! baseValue.name().equals( extendingValue.name())){thrownewIncorrectExtensionError("Base name[ "+ i +"] "+ baseValue.name()+" doesn't match with "+ extendingValue.name());}if(! baseValue.description().equals( extendingValue.description())){thrownewIncorrectExtensionError("Description[ "+ i +"] "+ baseValue.description()+" doesn't match with "+ extendingValue.description());}}}publicstaticclassIncorrectExtensionErrorextendsError{publicIncorrectExtensionError(finalString s ){super( s );}}}
@AxelAdvento A idéia aqui é que dependemos da interface Dayque possui o método valueOf()então switch(Day.valueOf()), é implementada por WeekDay, WeekEndDayenumerações.
Khaled Lela
3
Eu sugiro que você faça o contrário.
Em vez de estender a enumeração existente, crie uma maior e crie um subconjunto dela. Por exemplo, se você tivesse uma enumeração chamada PET e desejasse estendê-la para ANIMAL, faça o seguinte:
publicenum ANIMAL {
WOLF,CAT, DOG
}EnumSet<ANIMAL> pets =EnumSet.of(ANIMAL.CAT, ANIMAL.DOG);
Tenha cuidado, animais de estimação não são coleções imutáveis; você pode usar o Guava ou Java9 para obter mais segurança.
Tendo tido esse mesmo problema, gostaria de postar minha perspectiva. Eu acho que existem alguns fatores motivadores para fazer algo assim:
Você deseja ter alguns códigos de enumeração relacionados, mas em classes diferentes. No meu caso, eu tinha uma classe base com vários códigos definidos em um enum associado. Em uma data posterior (hoje!), Eu queria fornecer algumas novas funcionalidades para a classe base, o que também significou novos códigos para a enumeração.
A classe derivada suportaria a enumeração das classes base e a sua própria. Sem valores de enumeração duplicados! Então: como ter um enum para a subclasse que inclua o enum de seu pai junto com seus novos valores.
O uso de uma interface não resolve: você pode acidentalmente obter valores de enumeração duplicados. Não é desejável.
Acabei combinando as enumerações: isso garante que não haja valores duplicados, às custas de estar menos vinculado à sua classe associada. Mas achei que a questão duplicada era minha principal preocupação ...
Como um auxílio para entender por que estender um Enum não é razoável no nível de implementação de linguagem para considerar o que aconteceria se você passasse uma instância do Enum estendido para uma rotina que apenas entenda o Enum de base. Uma opção que o compilador prometeu ter todos os casos cobertos na verdade não cobriria esses valores estendidos do Enum.
Isso enfatiza ainda mais que os valores do Java Enum não são números inteiros, como os C, por exemplo: para usar um Java Enum como um índice de matriz, você deve solicitar explicitamente seu membro ordinal () para fornecer a um Java Enum um valor inteiro arbitrário que você deve adicionar um campo explícito para isso e referencie esse membro nomeado.
Este não é um comentário sobre o desejo do OP, apenas sobre o motivo pelo qual o Java nunca o fará.
Na esperança de que esta solução elegante de um colega meu seja vista neste longo post, eu gostaria de compartilhar essa abordagem para subclasses que segue a abordagem da interface e além.
Esteja ciente de que usamos exceções personalizadas aqui e esse código não será compilado, a menos que você o substitua por suas exceções.
A documentação é extensa e espero que seja compreensível para a maioria de vocês.
A interface que todo enum subclasse precisa implementar.
publicinterfaceParameter{/**
* Retrieve the parameters name.
*
* @return the name of the parameter
*/String getName();/**
* Retrieve the parameters type.
*
* @return the {@link Class} according to the type of the parameter
*/Class<?> getType();/**
* Matches the given string with this parameters value pattern (if applicable). This helps to find
* out if the given string is a syntactically valid candidate for this parameters value.
*
* @param valueStr <i>optional</i> - the string to check for
* @return <code>true</code> in case this parameter has no pattern defined or the given string
* matches the defined one, <code>false</code> in case <code>valueStr</code> is
* <code>null</code> or an existing pattern is not matched
*/boolean match(finalString valueStr);/**
* This method works as {@link #match(String)} but throws an exception if not matched.
*
* @param valueStr <i>optional</i> - the string to check for
* @throws ArgumentException with code
* <dl>
* <dt>PARAM_MISSED</dt>
* <dd>if <code>valueStr</code> is <code>null</code></dd>
* <dt>PARAM_BAD</dt>
* <dd>if pattern is not matched</dd>
* </dl>
*/void matchEx(finalString valueStr)throwsArgumentException;/**
* Parses a value for this parameter from the given string. This method honors the parameters data
* type and potentially other criteria defining a valid value (e.g. a pattern).
*
* @param valueStr <i>optional</i> - the string to parse the parameter value from
* @return the parameter value according to the parameters type (see {@link #getType()}) or
* <code>null</code> in case <code>valueStr</code> was <code>null</code>.
* @throws ArgumentException in case <code>valueStr</code> is not parsable as a value for this
* parameter.
*/Object parse(finalString valueStr)throwsArgumentException;/**
* Converts the given value to its external form as it is accepted by {@link #parse(String)}. For
* most (ordinary) parameters this is simply a call to {@link String#valueOf(Object)}. In case the
* parameter types {@link Object#toString()} method does not return the external form (e.g. for
* enumerations), this method has to be implemented accordingly.
*
* @param value <i>mandatory</i> - the parameters value
* @return the external form of the parameters value, never <code>null</code>
* @throws InternalServiceException in case the given <code>value</code> does not match
* {@link #getType()}
*/String toString(finalObject value)throwsInternalServiceException;}
A classe base ENUM de implementação.
publicenumParametersimplementsParameter{/**
* ANY ENUM VALUE
*/
VALUE(newParameterImpl<String>("VALUE",String.class,"[A-Za-z]{3,10}"));/**
* The parameter wrapped by this enum constant.
*/privateParameter param;/**
* Constructor.
*
* @param param <i>mandatory</i> - the value for {@link #param}
*/privateParameters(finalParameter param){this.param = param;}/**
* {@inheritDoc}
*/@OverridepublicString getName(){returnthis.param.getName();}/**
* {@inheritDoc}
*/@OverridepublicClass<?> getType(){returnthis.param.getType();}/**
* {@inheritDoc}
*/@Overridepublicboolean match(finalString valueStr){returnthis.param.match(valueStr);}/**
* {@inheritDoc}
*/@Overridepublicvoid matchEx(finalString valueStr){this.param.matchEx(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicObject parse(finalString valueStr)throwsArgumentException{returnthis.param.parse(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicString toString(finalObject value)throwsInternalServiceException{returnthis.param.toString(value);}}
A subclasse ENUM que "herda" da classe base.
publicenumExtendedParametersimplementsParameter{/**
* ANY ENUM VALUE
*/
VALUE(my.package.name.VALUE);/**
* EXTENDED ENUM VALUE
*/
EXTENDED_VALUE(newParameterImpl<String>("EXTENDED_VALUE",String.class,"[0-9A-Za-z_.-]{1,20}"));/**
* The parameter wrapped by this enum constant.
*/privateParameter param;/**
* Constructor.
*
* @param param <i>mandatory</i> - the value for {@link #param}
*/privateParameters(finalParameter param){this.param = param;}/**
* {@inheritDoc}
*/@OverridepublicString getName(){returnthis.param.getName();}/**
* {@inheritDoc}
*/@OverridepublicClass<?> getType(){returnthis.param.getType();}/**
* {@inheritDoc}
*/@Overridepublicboolean match(finalString valueStr){returnthis.param.match(valueStr);}/**
* {@inheritDoc}
*/@Overridepublicvoid matchEx(finalString valueStr){this.param.matchEx(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicObject parse(finalString valueStr)throwsArgumentException{returnthis.param.parse(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicString toString(finalObject value)throwsInternalServiceException{returnthis.param.toString(value);}}
Finalmente, o ParameterImpl genérico para adicionar alguns utilitários.
publicclassParameterImpl<T>implementsParameter{/**
* The default pattern for numeric (integer, long) parameters.
*/privatestaticfinalPattern NUMBER_PATTERN =Pattern.compile("[0-9]+");/**
* The default pattern for parameters of type boolean.
*/privatestaticfinalPattern BOOLEAN_PATTERN =Pattern.compile("0|1|true|false");/**
* The name of the parameter, never <code>null</code>.
*/privatefinalString name;/**
* The data type of the parameter.
*/privatefinalClass<T> type;/**
* The validation pattern for the parameters values. This may be <code>null</code>.
*/privatefinalPattern validator;/**
* Shortcut constructor without <code>validatorPattern</code>.
*
* @param name <i>mandatory</i> - the value for {@link #name}
* @param type <i>mandatory</i> - the value for {@link #type}
*/publicParameterImpl(finalString name,finalClass<T> type){this(name, type,null);}/**
* Constructor.
*
* @param name <i>mandatory</i> - the value for {@link #name}
* @param type <i>mandatory</i> - the value for {@link #type}
* @param validatorPattern - <i>optional</i> - the pattern for {@link #validator}
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>The default validation patterns {@link #NUMBER_PATTERN} or
* {@link #BOOLEAN_PATTERN} are applied accordingly.
* </dl>
*/publicParameterImpl(finalString name,finalClass<T> type,finalString validatorPattern){this.name = name;this.type = type;if(null!= validatorPattern){this.validator =Pattern.compile(validatorPattern);}elseif(Integer.class==this.type ||Long.class==this.type){this.validator = NUMBER_PATTERN;}elseif(Boolean.class==this.type){this.validator = BOOLEAN_PATTERN;}else{this.validator =null;}}/**
* {@inheritDoc}
*/@Overridepublicboolean match(finalString valueStr){if(null== valueStr){returnfalse;}if(null!=this.validator){finalMatcher matcher =this.validator.matcher(valueStr);return matcher.matches();}returntrue;}/**
* {@inheritDoc}
*/@Overridepublicvoid matchEx(finalString valueStr)throwsArgumentException{if(false==this.match(valueStr)){if(null== valueStr){throwArgumentException.createEx(ErrorCode.PARAM_MISSED,"The value must not be null",this.name);}throwArgumentException.createEx(ErrorCode.PARAM_BAD,"The value must match the pattern: "+this.validator.pattern(),this.name);}}/**
* Parse the parameters value from the given string value according to {@link #type}. Additional
* the value is checked by {@link #matchEx(String)}.
*
* @param valueStr <i>optional</i> - the string value to parse the value from
* @return the parsed value, may be <code>null</code>
* @throws ArgumentException in case the parameter:
* <ul>
* <li>does not {@link #matchEx(String)} the {@link #validator}</li>
* <li>cannot be parsed according to {@link #type}</li>
* </ul>
* @throws InternalServiceException in case the type {@link #type} cannot be handled. This is a
* programming error.
*/@Overridepublic T parse(finalString valueStr)throwsArgumentException,InternalServiceException{if(null== valueStr){returnnull;}this.matchEx(valueStr);if(String.class==this.type){returnthis.type.cast(valueStr);}if(Boolean.class==this.type){returnthis.type.cast(Boolean.valueOf(("1".equals(valueStr))||Boolean.valueOf(valueStr)));}try{if(Integer.class==this.type){returnthis.type.cast(Integer.valueOf(valueStr));}if(Long.class==this.type){returnthis.type.cast(Long.valueOf(valueStr));}}catch(finalNumberFormatException e){throwArgumentException.createEx(ErrorCode.PARAM_BAD,"The value cannot be parsed as "+this.type.getSimpleName().toLowerCase()+".",this.name);}returnthis.parseOther(valueStr);}/**
* Field access for {@link #name}.
*
* @return the value of {@link #name}.
*/@OverridepublicString getName(){returnthis.name;}/**
* Field access for {@link #type}.
*
* @return the value of {@link #type}.
*/@OverridepublicClass<T> getType(){returnthis.type;}/**
* {@inheritDoc}
*/@OverridepublicfinalString toString(finalObject value)throwsInternalServiceException{if(false==this.type.isAssignableFrom(value.getClass())){thrownewInternalServiceException(ErrorCode.PANIC,"Parameter.toString(): Bad type of value. Expected {0} but is {1}.",this.type.getName(),
value.getClass().getName());}if(String.class==this.type ||Integer.class==this.type ||Long.class==this.type){returnString.valueOf(value);}if(Boolean.class==this.type){returnBoolean.TRUE.equals(value)?"1":"0";}returnthis.toStringOther(value);}/**
* Parse parameter values of other (non standard types). This method is called by
* {@link #parse(String)} in case {@link #type} is none of the supported standard types (currently
* String, Boolean, Integer and Long). It is intended for extensions.
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>This default implementation always throws an InternalServiceException.
* </dl>
*
* @param valueStr <i>mandatory</i> - the string value to parse the value from
* @return the parsed value, may be <code>null</code>
* @throws ArgumentException in case the parameter cannot be parsed according to {@link #type}
* @throws InternalServiceException in case the type {@link #type} cannot be handled. This is a
* programming error.
*/protected T parseOther(finalString valueStr)throwsArgumentException,InternalServiceException{thrownewInternalServiceException(ErrorCode.PANIC,"ParameterImpl.parseOther(): Unsupported parameter type: "+this.type.getName());}/**
* Convert the values of other (non standard types) to their external form. This method is called
* by {@link #toString(Object)} in case {@link #type} is none of the supported standard types
* (currently String, Boolean, Integer and Long). It is intended for extensions.
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>This default implementation always throws an InternalServiceException.
* </dl>
*
* @param value <i>mandatory</i> - the parameters value
* @return the external form of the parameters value, never <code>null</code>
* @throws InternalServiceException in case the given <code>value</code> does not match
* {@link #getClass()}
*/protectedString toStringOther(finalObject value)throwsInternalServiceException{thrownewInternalServiceException(ErrorCode.PANIC,"ParameterImpl.toStringOther(): Unsupported parameter type: "+this.type.getName());}}
// enum A { a, b, c }staticfinalSet<Short> enumA =newLinkedHashSet<>(Arrays.asList(newShort[]{'a','b','c'}));// enum B extends A { d }staticfinalSet<Short> enumB =newLinkedHashSet<>(enumA);static{
enumB.add((short)'d');// If you have to add more elements:// enumB.addAll(Arrays.asList(new Short[]{ 'e', 'f', 'g', '♯', '♭' }));}
LinkedHashSetfornece que cada entrada existe apenas uma vez e que seu pedido é preservado. Se a ordem não importa, você pode usar HashSet. O código a seguir não é possível em Java:
for(A a : B.values()){// enum B extends A { d }switch(a){case a:case b:case c:System.out.println("Value is: "+ a.toString());break;default:thrownewIllegalStateException("This should never happen.");}}
O código pode ser escrito da seguinte maneira:
for(Short a : enumB){switch(a){case'a':case'b':case'c':System.out.println("Value is: "+newString(Character.toChars(a)));break;default:thrownewIllegalStateException("This should never happen.");}}
A partir do Java 7, você pode fazer o mesmo com String:
// enum A { BACKWARDS, FOREWARDS, STANDING }staticfinalSet<String> enumA =newLinkedHashSet<>(Arrays.asList(newString[]{"BACKWARDS","FOREWARDS","STANDING"}));// enum B extends A { JUMP }staticfinalSet<String> enumB =newLinkedHashSet<>(enumA);static{
enumB.add("JUMP");}
Usando a substituição de enum:
for(String a : enumB){switch(a){case"BACKWARDS":case"FOREWARDS":case"STANDING":System.out.println("Value is: "+ a);break;default:thrownewIllegalStateException("This should never happen.");}}
PrimaryColours
; é razoável querer Super de classe que isso EnumPrimaryAndPastelColours
pela adição de novos nomes de cores. Liskov ainda é o elefante na sala. Então, por que não começar com um Enum base de:AllMyColours
- E então pode-se subclassificar todas as cores para:PrimaryAndPastelColours
e subclasses subseqüentemente isso para:PrimaryColours
(mantendo a hierarquia em mente). O Java também não permitirá isso.Respostas:
Não, você não pode fazer isso em Java. Além de qualquer outra coisa,
d
presumivelmente seria uma instância deA
(dada a idéia normal de "estende"), mas usuários que apenas conheciamA
não sabiam disso - o que derrota o ponto de um enum ser um conjunto bem conhecido de valores.Se você puder nos dizer mais sobre como deseja usar isso, poderíamos sugerir soluções alternativas.
fonte
Object->A->B
em vez deObject->A->B extends Object
Enums representam uma enumeração completa de valores possíveis. Portanto, a resposta (inútil) é não.
Como exemplo de um problema real, considere os dias da semana, finais de semana e, a união, os dias da semana. Poderíamos definir todos os dias dentro de dias da semana, mas não poderíamos representar propriedades especiais para dias da semana e fins de semana.
O que poderíamos fazer é ter três tipos de enumeração com um mapeamento entre dias da semana / dias da semana e dias da semana.
Como alternativa, poderíamos ter uma interface aberta para o dia da semana:
Ou podemos combinar as duas abordagens:
fonte
A solução recomendada para isso é o padrão de enum extensível .
Isso envolve criar uma interface e usá-la onde você usa a enumeração. Em seguida, faça o enum implementar a interface. Você pode adicionar mais constantes, fazendo com que a nova enum também estenda a interface.
fonte
Nos bastidores, seu ENUM é apenas uma classe regular gerada pelo compilador. Essa classe gerada se estende
java.lang.Enum
. A razão técnica pela qual você não pode estender a classe gerada é que a classe gerada éfinal
. As razões conceituais para a sua finalização são discutidas neste tópico. Mas vou adicionar a mecânica à discussão.Aqui está uma enumeração de teste:
O código resultante do javap:
É possível que você possa digitar essa classe por conta própria e soltar a "final". Mas o compilador impede que você estenda "java.lang.Enum" diretamente. Você pode decidir NÃO estender o java.lang.Enum, mas sua classe e suas classes derivadas não seriam uma instância do java.lang.Enum ... o que pode não ser realmente importante para você!
fonte
pode ser escrito como:
Como pode ser útil: Digamos que queremos algo como: Temos eventos e estamos usando enumerações. Essas enumerações podem ser agrupadas por processamento semelhante. Se temos operação com muitos elementos, alguns eventos iniciam a operação, alguns são apenas um passo e outro finalizam a operação. Para reunir essa operação e evitar casos de comutação longos, podemos agrupá-los como no exemplo e usar:
Exemplo:
Adicione alguns mais avançados:
Acima, se tivermos alguma falha (myEvent.is (State_StatusGroup.FAIL)), iterando pelos eventos anteriores, podemos verificar facilmente se devemos reverter a transferência de dinheiro por:
Pode ser útil para:
fonte
Aqui está uma maneira de descobrir como estender uma enumeração para outra enumeração, é uma abordagem muito direta:
Suponha que você tenha um enum com constantes comuns:
então você pode tentar fazer um manual extendido desta maneira:
é claro que toda vez que você precisar estender uma constante, precisará modificar seus arquivos do SubEnum.
fonte
Caso você tenha perdido, há um capítulo no excelente livro de Joshua Bloch " Java Effective, 2nd edition ".
Extraia aqui .
Apenas a conclusão:
fonte
Eu tendem a evitar enums, porque eles não são extensíveis. Para ficar com o exemplo do OP, se A estiver em uma biblioteca e B em seu próprio código, você não poderá estender A se for um enum. É assim que às vezes substituo enums:
Existem alguns poços a serem evitados, veja os comentários no código. Dependendo das suas necessidades, essa é uma alternativa sólida e extensível às enums.
fonte
É assim que eu aprimoro o padrão de herança de enum com verificação de tempo de execução no inicializador estático. As
BaseKind#checkEnumExtender
verificações que "estendendo" a enum declaram todos os valores da enum base exatamente da mesma maneira,#name()
e#ordinal()
permanecem totalmente compatíveis.Ainda há copiar-colar envolvido para declarar valores, mas o programa falha rapidamente se alguém adiciona ou modifica um valor na classe base sem atualizar os estendidos.
Comportamento comum para enums diferentes que se estendem:
Enum base, com método de verificação:
Amostra de extensão:
fonte
Baseado em @Tom Hawtin - resposta simplificada que adicionamos suporte a switch,
fonte
valueOf()
método?Day
que possui o métodovalueOf()
entãoswitch(Day.valueOf())
, é implementada porWeekDay, WeekEndDay
enumerações.Eu sugiro que você faça o contrário.
Em vez de estender a enumeração existente, crie uma maior e crie um subconjunto dela. Por exemplo, se você tivesse uma enumeração chamada PET e desejasse estendê-la para ANIMAL, faça o seguinte:
Tenha cuidado, animais de estimação não são coleções imutáveis; você pode usar o Guava ou Java9 para obter mais segurança.
fonte
Tendo tido esse mesmo problema, gostaria de postar minha perspectiva. Eu acho que existem alguns fatores motivadores para fazer algo assim:
O uso de uma interface não resolve: você pode acidentalmente obter valores de enumeração duplicados. Não é desejável.
Acabei combinando as enumerações: isso garante que não haja valores duplicados, às custas de estar menos vinculado à sua classe associada. Mas achei que a questão duplicada era minha principal preocupação ...
fonte
Como um auxílio para entender por que estender um Enum não é razoável no nível de implementação de linguagem para considerar o que aconteceria se você passasse uma instância do Enum estendido para uma rotina que apenas entenda o Enum de base. Uma opção que o compilador prometeu ter todos os casos cobertos na verdade não cobriria esses valores estendidos do Enum.
Isso enfatiza ainda mais que os valores do Java Enum não são números inteiros, como os C, por exemplo: para usar um Java Enum como um índice de matriz, você deve solicitar explicitamente seu membro ordinal () para fornecer a um Java Enum um valor inteiro arbitrário que você deve adicionar um campo explícito para isso e referencie esse membro nomeado.
Este não é um comentário sobre o desejo do OP, apenas sobre o motivo pelo qual o Java nunca o fará.
fonte
Na esperança de que esta solução elegante de um colega meu seja vista neste longo post, eu gostaria de compartilhar essa abordagem para subclasses que segue a abordagem da interface e além.
Esteja ciente de que usamos exceções personalizadas aqui e esse código não será compilado, a menos que você o substitua por suas exceções.
A documentação é extensa e espero que seja compreensível para a maioria de vocês.
A interface que todo enum subclasse precisa implementar.
A classe base ENUM de implementação.
A subclasse ENUM que "herda" da classe base.
Finalmente, o ParameterImpl genérico para adicionar alguns utilitários.
fonte
Minha maneira de codificar seria a seguinte:
LinkedHashSet
fornece que cada entrada existe apenas uma vez e que seu pedido é preservado. Se a ordem não importa, você pode usarHashSet
. O código a seguir não é possível em Java:O código pode ser escrito da seguinte maneira:
A partir do Java 7, você pode fazer o mesmo com
String
:Usando a substituição de enum:
fonte