2011-12-28 9 views
5

que estoy tratando de hacer reverse lookup en algunas clases de enumeración de aplicación misma interfaz Field por iteración a través de lista de Class es el uso de guayaba Maps.uniqueIndex:Uso de medicamentos genéricos con colección de clases de enumeración implementar misma interfaz

Field valueOfSearchName = null; 
    for (final Class<? extends Enum<?>> clazz : ImmutableList.of(
     EntityField.class, 
     AddressField.class, 
     PersonFunctionType.class)) { 
    valueOfSearchName = Fields.valueOfSearchName(clazz, term.field()); // error 
    if (valueOfSearchName != null) { 
     // do something... 
     break; 
    } 
    } 

no quiero repetir mismo código (para la fabricación de índice y haciendo operaciones de búsqueda) en todas las clases de enumeración, así que usar ayudante de clase estática que contiene FieldsFields.valueOfSearchName método:

public static <E extends Enum<E> & Field> Field valueOfSearchName(
     final Class<E> clazz, final String searchName) { 
    // TODO: cache the index 
    final ImmutableMap<String, E> index = Maps.uniqueIndex(
     EnumSet.allOf(clazz), GET_SEARCH_NAME_FUNCTION); 
    return index.get(searchName); 
    } 

Desafortunadamente, Eclipse muestra un error:

Bound mismatch: 
The generic method valueOfSearchName(Class<E>, String) of type Fields is not 
applicable for the arguments (Class<capture#1-of ? extends Enum<?>>, String). 
The inferred type capture#1-of ? extends Enum<?> is not a valid substitute 
for the bounded parameter <E extends Enum<E> & Field> 

El problema es Class<? extends Enum<?>> clazz en fines de cada bucle (no coincidencia de campos), pero no sé cómo hacer frente a este caso (obviamente, no se puede agregar & Field al clazz).

+0

Este enlace puede ser útil http://stackoverflow.com/questions/7032941/why-is-this-type-not-a-valid-substitute-for-the-type-parameter – kosa

+1

Eclipse es famoso por estar en desacuerdo con sentido común. Verifique esta publicación: http://stackoverflow.com/questions/2220763/java-specific-enums-and-generic-enum-parameters –

+0

@baba No puedo reemplazar '?' Comodín con el parámetro de tipo nombrado 'T' en cada bucle, donde ocurre el problema ... Sin embargo [inspirado en la respuesta de John Skeet de esta pregunta] (http://stackoverflow.com/questions/5262096/how-do-i-get-the-value-of -an-enum-if-i-dont-know-the-class-at-compile-time) Cambié loop a 'for (@SuppressWarnings (" rawtypes ") final Class clazz: ...' y funcionó ...¿Es la forma correcta de hacer esto? – Xaerxess

Respuesta

0

Inspirado por Tom Hawtin's answer creé clase de contenedor que sostiene Class es, pero solo aquellos con firma <E extends Enum<E> & Field>:

public final static class FieldEnumWrapper<E extends Enum<E> & Field> { 
    private final Class<E> clazz; 
    private final ImmutableMap<String, E> index; 

    public static <E extends Enum<E> & Field> 
     FieldEnumWrapper<E> of(final Class<E> clazz) { 
    return new FieldEnumWrapper<E>(clazz); 
    } 

    private FieldEnumWrapper(final Class<E> clazz) { 
    this.clazz = clazz; 
    this.index = Maps.uniqueIndex(
     EnumSet.allOf(clazz), new Function<E, String>() { 
      @Override 
      public String apply(final E input) { 
      return input.searchName(); 
      } 
     }); 
    } 

    public Class<E> clazz() { 
    return clazz; 
    } 

    public Field valueOfSearchName(final String searchName) { 
    return index.get(searchName); 
    } 
} 

Ahora:

for (final FieldEnumWrapper<?> fieldEnum : ImmutableList.of(
    FieldEnumWrapper.of(EntityField.class), 
    FieldEnumWrapper.of(AddressField.class), 
    FieldEnumWrapper.of(PersonFunctionType.class))) { 
    valueOfSearchName = fieldEnum.valueOfSearchName("POD_I_OS_PARTNER"); 
    // ... 

es de tipo seguro y uso inadecuado de fábrica estática FieldEnumWrapper 's:

FieldEnumWrapper.of(NotEnumAndFieldClass.class) 

genera error de compilación.

Además, valueOfSearchName es ahora el método de FieldEnumWrapper lo que tiene más sentido que la clase de ayuda.

3

Considera Class<? extends List<?>. Class<? extends List<?> tiene dos comodines mientras que <E extends List<E>> Class<E> solo tiene un parámetro genérico. El primero admitirá Class<ArrayList<String>>. Entonces, sin hacer algo extra especial para las enumeraciones, los tipos no son compatibles.

¿Cómo corregirlo? ¡Una capa adicional de indirección!

public final class MetaEnum<E extends Enum<E>> { 
    private final E clazz; 
    public static <E extends Enum<E>> MetaEnum<E> of(E clazz) { 
     return clazz; 
    } 
    private MetaEnum(E clazz) { 
     this.clazz = clazz; 
    } 
    public E clazz() { 
     return clazz; 
    } 
    // ... 
} 

for (final MetaEnum<?> meta : ImmutableList.of(
    MetaEnum.of(EntityField  .class), 
    MetaEnum.of(AddressField  .class), 
    MetaEnum.of(PersonFunctionType.class) 
)) { 
    Field valueOfSearchName = Fields.valueOfSearchName(
     meta.clazz(), term.field() 
    ); 
    ... 

(Usual Dislaimer desbordamiento de pila:. No tanto como se trató de compilar)

0

tal vez algo como esto:

import java.util.*; 
class N { 
    static int n; 
} 
interface HasField { 
    int getField(); 
} 
enum Color implements HasField { 
    r, g, b; 
    public int getField() { 
     return field; 
    } 
    private int field = N.n++; 
} 
enum Day implements HasField { 
    m, t, w, th, f, sa, su; 
    public int getField() { 
     return field; 
    } 
    private int field = N.n++; 
} 
    class Helper { 
    Helper(Set<HasField> set) { 
     for (HasField hasField : set) 
      if (hasField instanceof Enum) { 
       Enum<?> e = (Enum<?>) hasField; 
       for (Object o : e.getDeclaringClass().getEnumConstants()) { 
        map.put(((HasField) o).getField(), (Enum<?>) o); 
       } 
      } else 
       throw new RuntimeException(hasField + " is not an enum!"); 
    } 
    final Map<Integer, Enum<?>> map = new TreeMap<Integer, Enum<?>>(); 
} 
public class Main { 
    public static void main(String[] args) { 
     Set<HasField> set = new LinkedHashSet<HasField>(); 
     set.add(Color.r); 
     set.add(Day.m); 
     Helper helper = new Helper(set); 
     for (int i = 0; i < N.n; i++) 
      System.out.println(i + " " + helper.map.get(i)); 
    } 
} 
+0

Creación de un conjunto de elementos en lugar de clases y combinación de 'e.getDeclaringClass(). getEnumConstants()' junto con 'Enum <-> HasField' no es legible y no es seguro para tipos ... Y también' set.add (nuevo HasField() {public int getField() {return 42;}}); 'es una ClassCastException en tiempo de ejecución. – Xaerxess

+0

agregó una prueba: hasField instanceof Enum –

+0

Aún así, prefiero las excepciones de tiempo de compilación en lugar del tiempo de ejecución;) – Xaerxess

Cuestiones relacionadas