views:

310

answers:

3

Hi all,

I have a pretty simple case where I do some basic generic assignment:

final Detail detail = field.getAnnotation(Detail.class);
final String example = detail.example();
final Class<?> type = field.getType();
if (List.class.isAssignableFrom(type))
                    ...
else if (Enum.class.isAssignableFrom(type))
    setValue(contract, field, Enum.valueOf(type, example));
else if (...)
.....

but the [Enum.valueOf()][1] is a bit difficult to call, in my case, the error is valueOf(java.lang.Class,java.lang.String) in java.lang.Enum cannot be applied to (java.lang.Class,java.lang.String)

This makes perfectly sense since type is Class. But since Enum is CRTP, I can't find a good way to cast type to make the compiler happy. Is using the raw type (Enum.valueOf((Class)type, example))) the only answer ? It gives me 2 warnings instead of only one.

Thanks for your help,

Nico.

[1]: http://www.j2ee.me/j2se/1.5.0/docs/api/java/lang/Enum.html#valueOf%28java.lang.Class, java.lang.String)

+1  A: 

You can write a helper method to capture a "T" type that satisfies Enum's requirement:

private <T extends Enum<T>> T helper(Class<?> type, String example) {
    return Enum.valueOf((Class<T>)type, example);
}

this should only have one warning

and then you can use it like

else if (Enum.class.isAssignableFrom(type))
    setValue(contract, field, helper(type, example));

Edit: okay, then how about:

private <T extends Enum<T>> Object helper(Class<?> type, String example) {
    return Enum.valueOf((Class<T>)type, example);
}
newacct
I still get the same error message, on the new helper. It makes sense, the same problem is transposed to the helper return type now.setValue() takes an Object as parameter. to simplify the case, final Object value = something(type, example); should work. And for now I can only do "something" with 2 warnings and by using raw types.
nraynaud
sorry, but your edit has no chance to work, the T is bound nowhere in the prototype, no inference can take place.
nraynaud
+1  A: 

The following line will do it with only one warning:

...
setValue( contract, field, Enum.valueOf( type.asSubclass( Enum.class ), example ) );
...
tangens
I buy your stuff, thanks.
nraynaud
+1  A: 

I don't think it's possible to remove the compiler warnings

The best case scenario is reducing all the errors into one like @tangens does.

Found two forum threads that show unsuccessful answers and explains the why a bit more.

So I put together a full example to demonstrate the issue as I see it.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.List;

public class Test {
  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.FIELD)
  public @interface Detail {
    String example();
  }

  public enum ExampleEnum {
    FOO_BAR, HELLO_WORLD
  }

  @Detail(example = "FOO_BAR")
  public ExampleEnum test;

  public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    populate(new Test());
  }

  public static void populate(Object o) throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {
    final Field field = o.getClass().getField("test");
    final Detail detail = field.getAnnotation(Detail.class);
    System.out.println("Annotation = " + detail);
    final String example = detail.example();
    final Class<?> type = field.getType();
    System.out.println("Field Class = " + type.getName());
    if (List.class.isAssignableFrom(type)) {
    } else if (Enum.class.isAssignableFrom(type)) {
      Class<? extends Enum> enumType = type.asSubclass(Enum.class); // Enum is a raw type. References to generic type Enum<E> should be parameterized
      Enum val = Enum.valueOf(enumType, example); // 1) Enum is a raw type. References to generic type Enum<E> should be parameterized
                                                  // 2) Type safety: Unchecked invocation valueOf(Class<capture#7-of ? extends Enum>, String) of the generic
                                                  //    method valueOf(Class<T>, String) of type Enum
      field.set(o, val);
    }
  }
}
TJ
Funny article on Enum. http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html
TJ