Java generics: Clean way to process two generic types using one function signature

Mär :

There is a class Utilities which offers public static functions, to do generic data processing. The class is mainly used to create FloatPointers to be used in native code.

The class looks like this:

class Utilities
{
    private Utilities(){}

    static FloatPointer toPointer( List< TypeA > list )
    {
        // do some magic
        return p
    }

    static FloatPointer toPointer( List< TypeB > list )
    {
        // do some different magic
        return p
    }
}

The problem with this is obvious. Because the compiler resolves these signatures to FloatPointer toPointer( List list ) I get an erasure issue, so these functions cannot be defined like this.

Now I am asking myself, what is the cleanest way to implement this.

Firstly, I would very much like to stick with the name toPointer instead of using varying function names. This is because in the real class there is a whole bunch of these toPointer functions dealing with various types. Introducing a new name will break the scheme and since other developers on the team are used to toPointer this is not a good idea.

I can imagine there to be two functions private static typeAToPointer( List< TypeA > list ) and private static typeBToPointer( List< TypeB > list ), but these two would need to be called from a public toPointer function as to not break the scheme of function names.

Sadly we do not implement TypeA or TypeB, so I cannot let the two implement a common interface and accept that - in general I cannot change TypeA or TypeB in any way.

Currently I tend to implement a function accepting a List< T > and simply check type, then deal with that. This routine would look something along the lines of this:

static < T > FloatPointer toPointer( List< T > list )
{
    Class clazz;
    if( !list.isEmpty() ) clazz = list.get( 0 ).getClass();
    else throw new IllegalArgumentException( "Empty list will result in NP." );

    if( clazz == TypeA.class ) return typeAToPointer( list );
    else if( clazz == TypeB.class ) return typeBToPointer( list );
    else throw new IllegalArgumentException( "List entries of invalid type." );

}

To me this looks like awful code. Am I mistaken there? What would be an elegant/clean way of achieving my goal, i.e. keep the function call toPointer, but process either generic list, all while being unable to temper with the definitions of TypeA or TypeB?

Michael Berry :

To me this looks like awful code. Am I mistaken there?

No, you're not mistaken. The example there is messy, unclear, performs unnecessary checks at runtime and isn't able to check any type safety at compile time either.

That being said:

What would be an elegant/clean way of achieving my goal, i.e. keep the function call toPointer, but process either generic list, all while being unable to temper with the definitions of TypeA or TypeB?

...you can't.

Java generics are implemented via erasure, so the generic information is all lost at runtime (there's no concept of it in the bytecode.) That means that you can't have two methods whose signatures differ only in their generic types, as the JVM would have no way of distinguishing between them at runtime.

Despite your reluctance, the cleanest, most obvious way of achieving what you're after is simply to relax your requirement that the method names must be identical. This, with very little change, gives you compile time type safety and much cleaner code than your example above.

Your only other option would be to create a non-generic subclass of List that implements each of the generic types you want to deal with, and use a different subclass for each type - but I really can't see any situation in which that would be preferable.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=132419&siteId=1