web-dev-qa-db-fra.com

Récupérer une énumération en utilisant 'valueOf' lève RuntimeException - Que faut-il utiliser à la place?

J'ai l'énumération suivante

enum Animal implements Mammal {
   CAT, DOG;

   public static Mammal findMammal(final String type) {
      for (Animal a : Animal.values()) {
         if (a.name().equals(type)) {
            return a;
         }
      }
   }
}

J'avais initialement utilisé la Enum.valueOf(Animal.class, "DOG"); pour trouver un animal en particulier. Cependant, je ne savais pas que si aucune correspondance n'était trouvée, une variable IllegalArgumentException était lancée. Je pensais que peut-être un null a été retourné. Cela me pose donc un problème. Je ne veux pas attraper cette IllegalArgumentException si une correspondance n'est pas trouvée. Je veux être capable de rechercher toutes les énumérations de type Mammal et je ne veux pas avoir à implémenter cette statique 'findMammal' pour chaque énumération de type Mammal. Ma question est donc la suivante: quelle serait la décision de conception la plus propice pour mettre en œuvre ce comportement? Je vais avoir un code d'appel comme ceci:

public class Foo {
   public Mammal bar(final String arg) {
      Mammal m = null;
      if (arg.equals("SomeValue")) {
         m = Animal.findMammal("CAT");
      } else if (arg.equals("AnotherValue") {
         m = Human.findMammal("BILL");
      }
      // ... etc
   }
}

Comme vous pouvez le constater, j'ai différents types de mammifères - «animaux», «humains», qui sont des enums. Je ne veux pas avoir à implémenter 'findMammal' pour chaque énumération de mammifères. Je suppose que le meilleur pari est simplement de créer une classe d’utilité qui prend un argument de mammifère et cherche cela? Peut-être qu'il y a une solution plus ordonnée.

11
Joeblackdev

Pourquoi ne pas créer un HashMap<String, Mammal>? Il suffit de le faire une fois ...

public class Foo {

  private static final Map<String, Mammal> NAME_TO_MAMMAL_MAP;

  static {
    NAME_TO_MAMMAL_MAP = new HashMap<String, Mammal>();
    for (Human human : EnumSet.allOf(Human.class)) {
      NAME_TO_MAMMAL_MAP.put(human.name(), human);
    }
    for (Animal animal : EnumSet.allOf(Animal.class)) {
      NAME_TO_MAMMAL_MAP.put(animal.name(), animal);
    }
  }

  public static Mammal bar(final String arg) {
    return NAME_TO_MAMMAL_MAP.get(arg);
  }
}

Remarques:

  • Cela retournera null si le nom n'existe pas
  • Cela ne détectera pas une collision de noms
  • Vous voudrez peut-être utiliser une carte immuable de certaines descriptions (par exemple, via Guava )
  • Vous voudrez peut-être écrire une méthode utilitaire pour créer une carte nom-valeur immuable pour une énumération générale, puis fusionner simplement les cartes :)
12
Jon Skeet

Vous pouvez utiliser la méthode getEnum Method dans la bibliothèque Apache EnumUtils . Il retourne l'énumération ou null s'il n'est pas trouvé.

EnumUtils.getEnum(Animal.class, "CAT");
9
feed.me

Si vous ne voulez pas toucher chaque Enum (compréhensible), une méthode utilitaire est la voie à suivre. Je comprends ne pas vouloir attraper l’exception dans votre code, mais l’attraper dans la méthode devrait bien se passer, cependant:

public static <T extends Enum<T>> T findEnumValue(Class<T> type, String name) {
    if (name == null)
        return null;
    try {
        return Enum.valueOf(type, name.toUpperCase());
    } catch (IllegalArgumentException iae) {
        return null;
    }
}
9
Dmitri

Vous pouvez utiliser la bibliothèque de goyave. Enums class permet cela:

Enums.valueOfFunction(MyEnum.class).apply(id);
8
Andrzej Polis

Je ne comprends pas la question mais j'avais également besoin d'une valueOf qui retourne null au lieu de lancer une exception. J'ai donc créé cette méthode d'utilitaire:

public static <T extends Enum<T>> T valueOf(T[] values, String name)
{
    for (T value : values) {
        if (value.name().equals(name)) {
            return value;
        }
    }
    return null;
}

Vous pourriez l'appeler comme ça:

Animal a = valueOf(Animal.values(), "CAT");
2
Ferran Maylinch

Vous avez une erreur dans votre code. Si vous avez besoin que votre fonction renvoie null lorsqu'elle ne trouve pas quelque chose, retournez-la simplement:

enum Animal implements Mammal {
   CAT, DOG;

   public static Mammal findMammal(final String type) {
      for (Animal a : Animal.values()) {
         if (a.name().equals(type)) {
            return a;
         }
      }
      return null; // this line works if nothing is found.
   }
}

Aucune technologie ne vous aidera si vous faites la même erreur et oubliez de créer une valeur de retour pour chaque cas possible.

Votre choix: Enum - EnumMaps - HashMap dépend de la dynamique de vos données. Les constantes Enums ne peuvent pas être créées au moment de l'exécution. D'autre part, vous n'avez pas besoin de vérifier beaucoup de choses au runtime - le compilateur le fera. Et vous oublierez très probablement de vérifier quelque chose. Ce que Jon Skeet gère facilement pourrait vous donner mal à la tête.

1
Gangnus

Si vous pouvez dire

m = Animal.findMammal("CAT");

Pourquoi ne peux-tu pas dire

m = Animal.CAT;

Le "CAT" devra quand même correspondre au nom de l'énum. Dans votre exemple, "CAT" n'est pas dérivé d'un paramètre mais est codé en dur.

UPDATE(peut-être plus au fait de la question)

C'est un peu élaboré, mais ...

public interface Mammal{

    public void doMammalStuff();

    public static interface Finder{
        public Mammal find(String name, Class<Enum<?>> enumType);
    }

    public static Finder FINDER = new Finder(){
        public Mammal find(String name, Class<Enum<?>> enumType) {
            return enumType.forName( name );
    };
    };
}

Ensuite, vous pouvez appeler

Mammal m = Mammal.Finder.find( "CAT", Animal.class );

Votre implémentation de find () peut avoir un aspect différent (c’est-à-dire ne pas lancer d’exception).

0
alex