Usar classes enum genéricos em mapas

xZise:

Tenho um mapa de nomes de classe para sua classe enum e eu tenho método que analisa uma cadeia como "SomeEnum.FIRST"para o objeto real. Mas Enum.valueOfnão aceita Class<? extends Enum<?>>enquanto o mapa não pode armazenar Class<T extends Enum<T>>.

Para o código, o mapa é algo como isto:

    private static final HashMap<String, Class<? extends Enum<?>>> enumsMap;

    static {
        enumsMap = new HashMap<>();
        // These are two DIFFERENT enum classes!
        registerEnum(SomeEnum.class);
        registerEnum(AnotherEnum.class);
    }

    private static void registerEnum(Class<? extends Enum<?>> enumClass) {
        enumsMap.put(enumClass.getSimpleName(), enumClass);
    }

E aqui está o analisador (código desnecessário removido):

    public <T extends Enum<T>> Object[] parse(List<String> strParameters) {
        Object[] parameters = new Object[strParameters.size()];
        for (int i = 0; i < parameters.length; i++) {
            String strParameter = strParameters.get(i);
            int delim = strParameter.lastIndexOf('.');
            String className = strParameter.substring(0, delim - 1);
            String enumName = strParameter.substring(delim + 1);
            Class<T> enumClass = (Class<T>) enumsMap.get(className);
            parameters[i] = Enum.valueOf(enumClass, enumName);
        }
        return parameters;
    }

E agora, se eu chamo isso parse, meu IDE (Estúdio Android) me diz, que "método não verificada 'parse (Lista)' invocação", e AFAIK isso é por causa desse tipo genérico. Se eu removê-lo parse, não seria compilar mas o aviso desaparece. Existe uma boa maneira de contornar isso?

Joshua Taylor:

Se você tem enums como:

  enum Foo {
    A, B, C
  }

  enum Bar {
    D, E, F
  }

Então você pode implementar o tipo de mapa que você está falando com o seguinte código.

class MyEnums {
    private final Map<String, Class<? extends Enum<?>>> map = new HashMap<>();

    public void addEnum(Class<? extends Enum<?>> e) {
      map.put(e.getSimpleName(), e);
    }

    private <T extends Enum<T>> T parseUnsafely(String name) {
      final int split = name.lastIndexOf(".");
      final String enumName = name.substring(0, split);
      final String memberName = name.substring(split + 1);
      @SuppressWarnings("unchecked")
      Class<T> enumType = (Class<T>) map.get(enumName);
      return Enum.valueOf(enumType, memberName);
    }

    public Object parse(String name) {
      return parseUnsafely(name);
    }

    public Object[] parseAll(String... names) {
      return Stream.of(names)
          .map(this::parse)
          .collect(toList())
          .toArray();
    }
  }

Isso não ficar em torno de um elenco desmarcado, embora; ele só esconde isso de você temporariamente. Você pode ver onde onde SuppressWarningsé usado para abafar o aviso sobre enumType. Em geral, é boa prática para aplicar a supressão de alerta em tão limitado um alcance possível. Neste caso, é para que única atribuição. Enquanto isso poderia ser uma bandeira vermelha em geral, no presente caso, sabemos que os únicos valores do mapa são, de fato, as classes de enumeração, uma vez que deve ter sido adicionado por addEnum.

Então, ele pode ser usado como:

  MyEnums me = new MyEnums();
  me.addEnum(Foo.class);
  me.addEnum(Bar.class);
  System.out.println(me.parse("Foo.A"));
  System.out.println(me.parse("Bar.E"));
  System.out.println(Arrays.toString(me.parseAll("Foo.B", "Bar.D", "Foo.C")));

que impressões:

A
E
[B, D, C]

Você vai notar que eu quebrei parseUnsafelye parseem métodos separados. A razão que nós não queremos expor parseUnsafelydiretamente é que faz uma garantia pelo seu tipo de retorno que não podemos realmente aplicar. Se fosse exposta, então poderíamos escrever código como

Bar bar = me.parseUnsafely("Foo.B");

que compila, mas falhar em tempo de execução com uma exceção de lançamento de classe.

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=187108&siteId=1
Recomendado
Clasificación