Search code examples
javagenericsinterfacecode-reuse

Reusable interface that uses generics


I want to create a single class that refers to a type of service using an interface. The service can have different implementations. The different implementations will process different types of requests. In the past I would define an interface something like this:

public interface I_Test
{
    public String get(String key, Enum type);
}

and implement it like this:

public class Test_1 implements I_Test
{
    public String get(String key, Enum type)
    {
        Enum_1 t1 = (Enum_1)type;

        switch(t1)
        {
            case NAME:
                return "Garry";
            case DOB:
                return "1966";
            default:
                throw new IllegalArgumentException("Unkown type [" + type + "]");
        }
    }
}

The good is I can use a different implementation of my interface to meet different needs. The bad is I have to type cast and so have a risk at runtime.

I was hoping that generics could solve this, so I did this:

public interface I_Test<T extends Enum>
{
    public String get(String key, T type);
}

and this:

public class Test_1 implements I_Test<Enum_1>
{
    public String get(String key, Enum_1 type)
    {
        switch(type)
        {
            case NAME:
                return "Garry";
            case DOB:
                return "1966";
            default:
                throw new IllegalArgumentException("Unkown type [" + type + "]");
        }
    }
}

but when I go to use the thing I get type safety warnings unless I declare my variable with the type I intend to use, like so:

I_Test<Enum_1> t1 = new Test_1();

This really bugs me because the whole point of creating the I_Test interface was so that I could use different implementations but it seems I have to lock in to a particular type at compile time to avoid this warning!

Is there any way to write a reusable interface that uses generics without this annoying warning?


Solution

  • The point of generics is to ensure that your code is more reliable (as far as type-safety is concerned). With generics, you are able to find out about type incompatibilities at compile-time instead of runtime. When you defined your interface as I_Test<T extends Enum>, you are basically saying that you do need the interface to be genericized according to a specific type. This is why Java is giving you a warning.

    You would get the same warning if you did something like this Map myMap = new HashMap<string>();.

    In Java, you actually specify the types and they are not inferred from what is on the RHS (unless you do something like Integer i = 1, but that's autoboxing). Since you genericized your interface, when you declare something using that interface, you need to specify the type to use (to genericize).

    When you instantiate a generic type, the compiler will translate those types by using something called "type erasure". Here, the compiler removes all information associated with the type parameters and type arguments. Java does this to maintain compatibility with older code that was written before Java had generics.

    So I_Test<Enum_1> is actually translated to the raw type I_Test during compilation. Using a raw type is generally considered to be a bad practice (hence, the "annoying warning"). The compiler is telling you that it does not have enough information to perform type-checking and therefore it cannot ensure type-safety (because you used a raw type).

    To learn more about generics, take a look at the following: