Xaerxess Xaerxess - 4 months ago 13
Java Question

Using generics with collection of enum classes implementing same interface

I am trying to do reverse lookup on few enum classes implementing same

Field
interface by iterating through list of
Class
es using Guava's
Maps.uniqueIndex
:

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;
}
}


I don't want to repeat same code (for making index and doing lookup) in all enum classes, so I use helper static class
Fields
containing
Fields.valueOfSearchName
method:

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);
}


Unfortunately, Eclipse shows an 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>





The problem is
Class<? extends Enum<?>> clazz
in for-each loop (not matching Field)
, but I don't know how to deal with this case (obviously I cannot add
& Field
to
clazz
).

Answer

Inspired by Tom Hawtin's answer I created wrapper class holding Classes, but only those with signature <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);
  }
}

Now:

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");
  // ...

is type-safe and inappropriate usage of FieldEnumWrapper's static factory:

FieldEnumWrapper.of(NotEnumAndFieldClass.class)

generates compile error.

Moreover, valueOfSearchName is now method of FieldEnumWrapper what make more sense that helper class.