sdyarnell
sdyarnell

Reputation: 69

Java Factory of Generic Classes

This question is similar to Factory and generics, and may have the same answer, but it is different. I have a generic base class that will be extended by classes in a completely separate JAR. Said JAR should be able to be dropped in and replaced without changing any other code (code is executed on command).

The base class:

public abstract class ThingBase<A> {

    public abstract A doThing();

    public abstract Thing getThingType();

    // This might be the wrong way to do this, and I elaborate below.
    public final SortedMap<Long, A> getCollectionContainer() {
        return new TreeMap<Long, A>();
    }

}

I have an enum that I will be creating different thing extensions for each entry.

public enum Thing { T1, T2, T3 }

The factory creating such objects:

public class ThingFactory {
    private static final Map<Thing, Class<? extends ThingInterface<?>>> thingMap;

    static{//populate map by finding all extensions of base class}

    public static ThingBase<?> getThing(Thing thing) {
        try {
            Class<? extends ThingBase<?>> thingImpl = thingMap.get(thing);
            if (thingImpl != null) {
                return thingImpl.newInstance();
            } else {
                return null;
            }
        } catch (InstantiationException e) {
            return null;
        } catch (IllegalAccessException e) {
            return null;
        }
    }
}

My issue is with the unchecked and raw type warnings in the below code (note not above, above code compiles just fine). In the following usage, the code executes successfully, but I get unchecked type warnings.

public class ThingAccumulator {
    // Raw Type
    private final ThingBase myThing;

    // Raw Type
    private SortedMap myMap;

    public ThingAccumulator(Thing thing) {
        myThing = ThingFactory.getThing(thing);
        myMap = myThing.getCollectionData();
        //Type safety: The method put(Object, Object) belongs to the raw type Map. References to generic type Map<K,V> should be parameterized
        myMap.put(System.currentTimeMillis(), myThing.doThing());
    }
}

Putting < ? > on both ThingBase and < Long, ?> on SortedMap fields complains about the ?s are different:

The method put(Long, capture#17-of ?) in the type Map is not applicable for the arguments (long, capture#18-of ?)

Now the major issue here seems to be that the compiler cannot assume the ? and ? are the same, which is fair, but the only solution I have come up with so far is to put the type in the holding class, which isnt an option because only the implementation knows the data type :/

Does anyone have ANY idea on how to rid of these warnings? Is there a better way to be doing this?

Thanks,

SDY

Upvotes: 2

Views: 1068

Answers (1)

Dan Getz
Dan Getz

Reputation: 9135

You can add a generic type parameter to ThingAccumulator, use that to move things around so that the only code Java complains about is one simple line that you can prove truly is type-safe, then suppress that warning because you know it is not a problem.

Giving a type parameter to ThingAccumulator lets its fields be type checked when used together. You can push the question of "but what's the type?" outside of the constructor by taking a ThingBase<A> instance as the parameter instead of a Thing enum value. Then, a static factory method can handle the object construction:

public class ThingAccumulator<A> {
    private final ThingBase<A> myThing;

    private SortedMap<Long, A> myMap;

    public static ThingAccumulator<?> create(Thing thing) {
        return new ThingAccumulator(ThingFactory.getThing(thing));
    }

    protected ThingAccumulator(ThingBase<A> thing) {
        myThing = thing;
        myMap = myThing.getCollectionData();
        myMap.put(System.currentTimeMillis(), myThing.doThing());
    }
}

I wish Java would like the create() function. The logic could go something like this:

  • ThingFactory.getThing() returns a ThingBase<?>. That's like a ThingBase<X> for some unknown X. Just because we don't know it doesn't mean it doesn't exist.
  • new ThingAccumulator() can take a ThingBase<X>, for any X, and return a ThingAccumulator<X>.
  • Now we have a ThingAccumulator<?> — an instance with an unknown type parameter. Type safety hasn't been broken.

However, Java doesn't like it (at least in my version of Java 1.7.0). But if we like it, we can suppress the warning:

    @SuppressWarnings("unchecked")
    public static ThingAccumulator<?> create(Thing thing) {
        return new ThingAccumulator(ThingFactory.getThing(thing));
    }

And because all the rest of the code (so far…) is type-safe, no more warnings.

Upvotes: 1

Related Questions