8

I have a method createFoo() that creates instances of Foo<T> using the Class<T> instance for that T. Now I want to extend that method to forward calls that are made using an enum type to the method createEnumFoo(). Calling the second method from the first one seems to be non-trivial. Below is an example of how I managed to do it using two unchecked casts and an extra method, all of which I would like to get rid of.

The method castEnumType() is required because I couldn't find a way to cast a Class<?> to a Class<E extends Enum<E>> without having the E bound somewhere. This involves an unchecked cast because I have not found a way to do it using Class.asSubclass(). After creating the instance of Foo, I need to cast it from Foo<E> to Foo<T> event though E and T will always be the same types.

I can't weaken the signature of createEnumFoo() because it is calling Enum.valueOf(enumType, ...) and requires the result of this to be of type E.

final class Example {
    <E extends Enum<E>> Foo<E> createEnumFoo(Class<E> enumType) {
        // This makes use of e.g. Enum.valueOf(enumType, ...).
        return null;
    }

    <E extends Enum<E>> Class<E> castEnumType(Class<?> enumType) {
        return (Class<E>) enumType;
    }

    <T> Foo<T> createFoo(Class<T> type) {
        if (Enum.class.isAssignableFrom(type))
            return (Foo<T>) createEnumFoo(castEnumType(type));
        else
            // Here we would do something else or maybe throw an exception.
            return null;
    }

    interface Foo<T> {
    }
}

Is there a simpler way to do this?


Some context

To clarify the problem I'm facing, I'll explain how this problem actually arose in a project I'm working on:

In the code where I came across this problem, Foo<T> is actually Converter<T>, which is an interface which allows an instance of T to be serialized and de-serialized from and to a JSON value:

public interface Converter<T> {
    JsonObject encode(T value);

    T decode(JsonObject data);
} 

And createFoo() is actually a method converterForType() which takes a Class<T> instance and dynamically dispatches to a bunch of static methods and fields that create/contain converters for common Java types and types specific to the project. Normally when a converter is needed, the appropriate method/field is accessed directly but there are some places where the type is only known at runtime, which is where converterForType() is used.

Now I wanted to extend that method to automatically handle enum types by converting those to JSON strings containing the name of the enum constant. This is why I need to call the method enumConverter() from converterForType(). This is the implementation of enumConverter():

public static <E extends Enum<E>> Converter<E> enumConverter(final Class<E> enumClass) {
    return new Converter<E>() {
        public JsonObject encode(E value) {
            return Json.convert(value.name());
        }

        public E decode(JsonObject data) {
            return Enum.valueOf(enumClass, data.asString());
        }
    };
}
Feuermurmel
  • 9,490
  • 10
  • 60
  • 90
  • 1
    I'm not sure there's a better way. See this related post: [Passing a runtime resolved parameter to a method which has multiple bound type, compilation error](http://stackoverflow.com/questions/15978730/passing-a-runtime-resolved-parameter-to-a-method-which-has-multiple-bound-type). How are you actually returning the enum instance though? Depending, you could just use `Class.getEnumConstants()`. Let me know if I should expand on that with an answer. – Paul Bellora Mar 14 '14 at 15:01
  • Can you show an example of creating the `Foo` with the Enum class? – Radiodef Mar 15 '14 at 21:20
  • @PaulBellora @Radiodef I added a description of where this problem actually came up in a project. Accessing the enum constants from given the `Class` instance is not an issue, if I call `enumConverter()` directly. – Feuermurmel Mar 15 '14 at 21:35

1 Answers1

2

What about this, use raw types for createEnumFoo method
Edit: fixed compile error reported by @Feuermurmel in comments

@SuppressWarnings({ "unchecked", "rawtypes" })
final class Example
{
    <E extends Enum<E>> Foo<E> createEnumFoo(Class enumType)
    {
        // This makes use of e.g. Enum.valueOf(enumType, ...).
        Enum x = Enum.valueOf(enumType, "x");
        return (Foo<E>) x;
    }

    <T extends Enum> Foo<T> createFoo(Class<T> type)
    {
        if (Enum.class.isAssignableFrom(type))
            return (Foo<T>) createEnumFoo(type);
        else
            // Here we would do something else or maybe throw an exception.
            return null;
    }

    interface Foo<T>
    {
    }
}
Ahmed Qasid
  • 293
  • 2
  • 9
  • Actually, you are right. I can probably just as well use raw types if I can't avoid unchecked casts. But I instead added a cast to `Class` at the call site of `createEnumFoo()` instead of weakening that method's signature: `return (Foo) createEnumFoo((Class) type);`. BTW: `createEnumFoo()` cannot return an `Enum>`, as you do in your code, it needs to return some `Foo`. Can you fix that? – Feuermurmel Mar 15 '14 at 21:40
  • My code compiles correctly in eclipse (java 7). `> Foo createEnumFoo(Class enumType) { // This makes use of e.g. Enum.valueOf(enumType, ...). Foo foo =Enum.valueOf(enumType, "x"); return foo; }` – Ahmed Qasid Mar 16 '14 at 10:23
  • When I copy and paste the code in your answer into a file called `Example.java` and run `javac Example.java`, it prints the following error: `Example.java:7: error:incompatible types`. See here: https://ideone.com/zaYv7B – Feuermurmel Mar 16 '14 at 13:48
  • 1
    I know it's beent four years, but I fixed the compile error :) – Ahmed Qasid Jun 26 '18 at 20:17