Lorsque je travaille avec des variables/paramètres qui ne peuvent prendre qu'un nombre fini de valeurs, j'essaie de toujours utiliser le enum
de Java, comme dans
public enum BonusType {
MONTHLY, YEARLY, ONE_OFF
}
Tant que je reste dans mon code, cela fonctionne bien. Cependant, j’ai souvent besoin d’interfacer avec un autre code qui utilise des valeurs simples int
(ou String
) dans le même but, ou j’ai besoin de lire/écrire de/vers une base de données où se trouvent les données. stocké sous forme de nombre ou de chaîne.
Dans ce cas, j'aimerais avoir un moyen pratique d'associer chaque valeur enum à un entier, de sorte que je puisse convertir les deux manières (en d'autres termes, j'ai besoin d'une "énumération réversible").
Aller d’enum à int est facile:
public enum BonusType {
public final int id;
BonusType(int id) {
this.id = id;
}
MONTHLY(1), YEARLY(2), ONE_OFF(3);
}
Ensuite, je peux accéder à la valeur int comme BonusType x = MONTHLY; int id = x.id;
.
Cependant, je ne vois pas de bonne façon de faire l’inverse, c’est-à-dire d’int. Idéalement, quelque chose comme
BonusType bt = BonusType.getById(2);
Les seules solutions que je pourrais trouver sont:
BonusType.values()
pour remplir une carte "int -> enum", puis la met en cache et l'utilise pour les recherches. Cela fonctionnerait, mais je devrais copier cette méthode à l'identique dans chaque énumération que j'utilise :-(.Les deux méthodes semblent terriblement gênantes pour un problème aussi simple (?).
D'autres idées/idées?
http://www.javaspecialists.co.za/archive/Issue113.html
La solution est similaire à la vôtre avec une valeur int dans la définition de l’énumération. Il crée ensuite un utilitaire de recherche basé sur les génériques:
public class ReverseEnumMap<V extends Enum<V> & EnumConverter> {
private Map<Byte, V> map = new HashMap<Byte, V>();
public ReverseEnumMap(Class<V> valueType) {
for (V v : valueType.getEnumConstants()) {
map.put(v.convert(), v);
}
}
public V get(byte num) {
return map.get(num);
}
}
Cette solution est agréable et ne nécessite pas de "tripoter la réflexion" car elle est basée sur le fait que tous les types enum héritent implicitement de l'interface Enum.
yourEnum.ordinal()
EnumType.values()[someInt]
EnumType.valueOf(yourString)
yourEnum.name()
Une note de côté:
Comme vous l'avez fait remarquer avec justesse, la ordinal()
peut être "instable" de version en version. C'est la raison exacte pour laquelle je stocke toujours des constantes sous forme de chaînes dans mes bases de données. (En fait, lorsque j'utilise MySql, je les stocke sous la forme MySql enums !)
J'ai trouvé cela sur le Web, c'était très utile et simple à mettre en œuvre. Cette solution n'a pas été faite par moi
http://www.ajaxonomy.com/2007/Java/making-the-most-of-Java-50-enum-tricks
public enum Status {
WAITING(0),
READY(1),
SKIPPED(-1),
COMPLETED(5);
private static final Map<Integer,Status> lookup
= new HashMap<Integer,Status>();
static {
for(Status s : EnumSet.allOf(Status.class))
lookup.put(s.getCode(), s);
}
private int code;
private Status(int code) {
this.code = code;
}
public int getCode() { return code; }
public static Status get(int code) {
return lookup.get(code);
}
}
Il semble que les réponses à cette question soient obsolètes depuis la publication de Java 8.
public enum AccessLevel {
PRIVATE("private", 0),
PUBLIC("public", 1),
DEFAULT("default", 2);
AccessLevel(final String name, final int value) {
this.name = name;
this.value = value;
}
private final String name;
private final int value;
public String getName() {
return name;
}
public int getValue() {
return value;
}
static final Map<String, AccessLevel> names = Arrays.stream(AccessLevel.values())
.collect(Collectors.toMap(AccessLevel::getName, Function.identity()));
static final Map<Integer, AccessLevel> values = Arrays.stream(AccessLevel.values())
.collect(Collectors.toMap(AccessLevel::getValue, Function.identity()));
public static AccessLevel fromName(final String name) {
return names.get(name);
}
public static AccessLevel fromValue(final int value) {
return values.get(value);
}
}
Pour me sauver en écrivant des charges de code passe-partout ou de code en double pour chaque enum, j'ai utilisé à la place ValuedEnum
d'Apache Commons Lang.
Définition:
public class NRPEPacketType extends ValuedEnum {
public static final NRPEPacketType TYPE_QUERY = new NRPEPacketType( "TYPE_QUERY", 1);
public static final NRPEPacketType TYPE_RESPONSE = new NRPEPacketType( "TYPE_RESPONSE", 2);
protected NRPEPacketType(String name, int value) {
super(name, value);
}
}
tilisation:
int -> ValuedEnum:
NRPEPacketType packetType =
(NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1);
Vous pourriez peut-être utiliser quelque chose comme
interface EnumWithId {
public int getId();
}
enum Foo implements EnumWithId {
...
}
Cela réduirait le besoin de réflexion dans votre classe de services publics.
Dans ce code, pour une recherche permanente et intense, disposez de la mémoire ou du processus à utiliser, et je sélectionne la mémoire avec la matrice de conversion comme index. J'espère que c'est utile
public enum Test{
VALUE_ONE(101, "Im value one"),
VALUE_TWO(215, "Im value two");
private final int number;
private final byte[] desc;
private final static int[] converter = new int[216];
static{
Test[] st = values();
for(int i=0;i<st.length;i++){
cv[st[i].number]=i;
}
}
Test(int value, byte[] description) {
this.number = value;
this.desc = description;
}
public int value() {
return this.number;
}
public byte[] description(){
return this.desc;
}
public static String description(int value) {
return values()[converter[rps]].desc;
}
public static Test fromValue(int value){
return values()[converter[rps]];
}
}
Utilisez une interface pour lui montrer qui est le patron.
public interface SleskeEnum {
int id();
SleskeEnum[] getValues();
}
public enum BonusType implements SleskeEnum {
MONTHLY(1), YEARLY(2), ONE_OFF(3);
public final int id;
BonusType(int id) {
this.id = id;
}
public SleskeEnum[] getValues() {
return values();
}
public int id() { return id; }
}
public class Utils {
public static SleskeEnum getById(SleskeEnum type, int id) {
for(SleskeEnum t : type.getValues())
if(t.id() == id) return t;
throw new IllegalArgumentException("BonusType does not accept id " + id);
}
public static void main(String[] args) {
BonusType shouldBeMonthly = (BonusType)getById(BonusType.MONTHLY,1);
System.out.println(shouldBeMonthly == BonusType.MONTHLY);
BonusType shouldBeMonthly2 = (BonusType)getById(BonusType.MONTHLY,1);
System.out.println(shouldBeMonthly2 == BonusType.YEARLY);
BonusType shouldBeYearly = (BonusType)getById(BonusType.MONTHLY,2);
System.out.println(shouldBeYearly == BonusType.YEARLY);
BonusType shouldBeOneOff = (BonusType)getById(BonusType.MONTHLY,3);
System.out.println(shouldBeOneOff == BonusType.ONE_OFF);
BonusType shouldException = (BonusType)getById(BonusType.MONTHLY,4);
}
}
Et le résultat:
C:\Documents and Settings\user\My Documents>Java Utils
true
false
true
true
Exception in thread "main" Java.lang.IllegalArgumentException: BonusType does not accept id 4
at Utils.getById(Utils.Java:6)
at Utils.main(Utils.Java:23)
C:\Documents and Settings\user\My Documents>
Les .ordinal()
et values()[i]
sont instables car ils dépendent de l'ordre des énumérations. Ainsi, si vous modifiez l’ordre des énumérations ou si vous en ajoutez/en supprimez quelques-unes, votre programme serait interrompu.
Voici une méthode simple mais efficace pour mapper entre enum et int.
public enum Action {
ROTATE_RIGHT(0), ROTATE_LEFT(1), RIGHT(2), LEFT(3), UP(4), DOWN(5);
public final int id;
Action(int id) {
this.id = id;
}
public static Action get(int id){
for (Action a: Action.values()) {
if (a.id == id)
return a;
}
throw new IllegalArgumentException("Invalid id");
}
}
Son application pour les chaînes ne devrait pas être difficile.
Un exemple d'utilisation très propre de reverse Enum
Étape 1 Définissez un interface
EnumConverter
public interface EnumConverter <E extends Enum<E> & EnumConverter<E>> {
public String convert();
E convert(String pKey);
}
Étape 2
Créez un nom de classe ReverseEnumMap
import Java.util.HashMap;
import Java.util.Map;
public class ReverseEnumMap<V extends Enum<V> & EnumConverter<V>> {
private Map<String, V> map = new HashMap<String, V>();
public ReverseEnumMap(Class<V> valueType) {
for (V v : valueType.getEnumConstants()) {
map.put(v.convert(), v);
}
}
public V get(String pKey) {
return map.get(pKey);
}
}
Étape 3
Allez à vous Enum
class et implement
le avec EnumConverter<ContentType>
et bien sûr substituer les méthodes d'interface. Vous devez également initialiser un ReverseEnumMap statique.
public enum ContentType implements EnumConverter<ContentType> {
VIDEO("Video"), GAME("Game"), TEST("Test"), IMAGE("Image");
private static ReverseEnumMap<ContentType> map = new ReverseEnumMap<ContentType>(ContentType.class);
private final String mName;
ContentType(String pName) {
this.mName = pName;
}
String value() {
return this.mName;
}
@Override
public String convert() {
return this.mName;
}
@Override
public ContentType convert(String pKey) {
return map.get(pKey);
}
}
Étape 4
Créez maintenant un fichier de classe Communication
et appelez la nouvelle méthode pour convertir un Enum
en String
et String
en Enum
. Je viens de mettre la méthode principale à des fins d'explication.
public class Communication<E extends Enum<E> & EnumConverter<E>> {
private final E enumSample;
public Communication(E enumSample) {
this.enumSample = enumSample;
}
public String resolveEnumToStringValue(E e) {
return e.convert();
}
public E resolveStringEnumConstant(String pName) {
return enumSample.convert(pName);
}
//Should not put main method here... just for explanation purpose.
public static void main(String... are) {
Communication<ContentType> comm = new Communication<ContentType>(ContentType.GAME);
comm.resolveEnumToStringValue(ContentType.GAME); //return Game
comm.resolveStringEnumConstant("Game"); //return GAME (Enum)
}
}
Je ne sais pas si c'est la même chose en Java, mais les types enum en C sont automatiquement mappés sur des entiers, vous pouvez donc utiliser le type ou l'entier pour y accéder. Avez-vous déjà essayé d'y accéder avec un entier?
Par souci d'exhaustivité, voici une approche générique pour extraire les valeurs enum par index à partir de n'importe quel type enum. Mon intention était de donner à la méthode l'apparence suivante: Enum.valueOf (Class, String) . Fyi, j'ai copié cette méthode de ici .
Les problèmes liés aux index (déjà discutés en profondeur ici) sont toujours d'actualité.
/**
* Returns the {@link Enum} instance for a given ordinal.
* This method is the index based alternative
* to {@link Enum#valueOf(Class, String)}, which
* requires the name of an instance.
*
* @param <E> the enum type
* @param type the enum class object
* @param ordinal the index of the enum instance
* @throws IndexOutOfBoundsException if ordinal < 0 || ordinal >= enums.length
* @return the enum instance with the given ordinal
*/
public static <E extends Enum<E>> E valueOf(Class<E> type, int ordinal) {
Preconditions.checkNotNull(type, "Type");
final E[] enums = type.getEnumConstants();
Preconditions.checkElementIndex(ordinal, enums.length, "ordinal");
return enums[ordinal];
}
Vraiment bonne question :-) J'ai utilisé une solution similaire à celle de Mr.Ferguson il y a quelque temps. Notre enum décompilé ressemble à ceci:
final class BonusType extends Enum
{
private BonusType(String s, int i, int id)
{
super(s, i);
this.id = id;
}
public static BonusType[] values()
{
BonusType abonustype[];
int i;
BonusType abonustype1[];
System.arraycopy(abonustype = ENUM$VALUES, 0, abonustype1 = new BonusType[i = abonustype.length], 0, i);
return abonustype1;
}
public static BonusType valueOf(String s)
{
return (BonusType)Enum.valueOf(BonusType, s);
}
public static final BonusType MONTHLY;
public static final BonusType YEARLY;
public static final BonusType ONE_OFF;
public final int id;
private static final BonusType ENUM$VALUES[];
static
{
MONTHLY = new BonusType("MONTHLY", 0, 1);
YEARLY = new BonusType("YEARLY", 1, 2);
ONE_OFF = new BonusType("ONE_OFF", 2, 3);
ENUM$VALUES = (new BonusType[] {
MONTHLY, YEARLY, ONE_OFF
});
}
}
En voyant cela, on comprend pourquoi ordinal()
est instable. C'est le i
dans super(s, i);
. Je suis également pessimiste sur le fait que vous puissiez penser à une solution plus élégante que celles que vous avez déjà énumérées. Après toutes les énumérations sont des classes comme des classes finales.
Int -->String :
public enum Country {
US("US",0),
UK("UK",2),
DE("DE",1);
private static Map<Integer, String> domainToCountryMapping;
private String country;
private int domain;
private Country(String country,int domain){
this.country=country.toUpperCase();
this.domain=domain;
}
public String getCountry(){
return country;
}
public static String getCountry(String domain) {
if (domainToCountryMapping == null) {
initMapping();
}
if(domainToCountryMapping.get(domain)!=null){
return domainToCountryMapping.get(domain);
}else{
return "US";
}
}
private static void initMapping() {
domainToCountryMapping = new HashMap<Integer, String>();
for (Country s : values()) {
domainToCountryMapping.put(s.domain, s.country);
}
}
donné:
public enum BonusType {MENSUEL (0), ANNUEL (1), ONE_OFF (2)}
BonusType bonus = YEARLY;
System.out.println (bonus.Ordinal () + ":" + bonus)
Sortie: 1: tous les ans
Juste parce que la réponse acceptée n'est pas autonome:
Code d'assistance:
public interface EnumWithCode<E extends Enum<E> & EnumWithCode<E>> {
public Integer getCode();
E fromCode(Integer code);
}
public class EnumWithCodeMap<V extends Enum<V> & EnumWithCode<V>> {
private final HashMap<Integer, V> _map = new HashMap<Integer, V>();
public EnumWithCodeMap(Class<V> valueType) {
for( V v : valueType.getEnumConstants() )
_map.put(v.getCode(), v);
}
public V get(Integer num) {
return _map.get(num);
}
}
Exemple d'utilisation:
public enum State implements EnumWithCode<State> {
NOT_STARTED(0), STARTED(1), ENDED(2);
private static final EnumWithCodeMap<State> map = new EnumWithCodeMap<State>(
State.class);
private final int code;
private State(int code) {
this.code = code;
}
@Override
public Integer getCode() {
return code;
}
@Override
public State fromCode(Integer code) {
return map.get(code);
}
}
J'avais besoin de quelque chose de différent parce que je voulais utiliser une approche générique. Je lis l'énumération vers et à partir des tableaux d'octets. C'est là que je viens avec:
public interface EnumConverter {
public Number convert();
}
public class ByteArrayConverter {
@SuppressWarnings("unchecked")
public static Enum<?> convertToEnum(byte[] values, Class<?> fieldType, NumberSystem numberSystem) throws InvalidDataException {
if (values == null || values.length == 0) {
final String message = "The values parameter must contain the value";
throw new IllegalArgumentException(message);
}
if (!dtoFieldType.isEnum()) {
final String message = "dtoFieldType must be an Enum.";
throw new IllegalArgumentException(message);
}
if (!EnumConverter.class.isAssignableFrom(fieldType)) {
final String message = "fieldType must implement the EnumConverter interface.";
throw new IllegalArgumentException(message);
}
Enum<?> result = null;
Integer enumValue = (Integer) convertToType(values, Integer.class, numberSystem); // Our enum's use Integer or Byte for the value field.
for (Object enumConstant : fieldType.getEnumConstants()) {
Number ev = ((EnumConverter) enumConstant).convert();
if (enumValue.equals(ev)) {
result = (Enum<?>) enumConstant;
break;
}
}
if (result == null) {
throw new EnumConstantNotPresentException((Class<? extends Enum>) fieldType, enumValue.toString());
}
return result;
}
public static byte[] convertEnumToBytes(Enum<?> value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
if (!(value instanceof EnumConverter)) {
final String message = "dtoFieldType must implement the EnumConverter interface.";
throw new IllegalArgumentException(message);
}
Number enumValue = ((EnumConverter) value).convert();
byte[] result = convertToBytes(enumValue, requiredLength, numberSystem);
return result;
}
public static Object convertToType(byte[] values, Class<?> type, NumberSystem numberSystem) throws InvalidDataException {
// some logic to convert the byte array supplied by the values param to an Object.
}
public static byte[] convertToBytes(Object value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
// some logic to convert the Object supplied by the'value' param to a byte array.
}
}
Exemple d'enum:
public enum EnumIntegerMock implements EnumConverter {
VALUE0(0), VALUE1(1), VALUE2(2);
private final int value;
private EnumIntegerMock(int value) {
this.value = value;
}
public Integer convert() {
return value;
}
}
public enum EnumByteMock implements EnumConverter {
VALUE0(0), VALUE1(1), VALUE2(2);
private final byte value;
private EnumByteMock(int value) {
this.value = (byte) value;
}
public Byte convert() {
return value;
}
}