简体   繁体   中英

Java factory pattern using generic constraints

I have

public class  MyFactory()
{
   public static <T> T getItem(Element element, Class<T> clazz)
   {
         T item = null;
         if (clazz == IFoo.class)
         {
              item =  (T) new Foo();
         }
         else if (clazz == IBar.class)
         {
              item =  (T) new Bar();
         }
         ...

        assert item instanceof IParsable;
        (IParsable(foo)).parse(element)

       return item;
   }

}

and then I call it like this

IFoo parsedFoo = MyFactory.getItem(someElement, IFoo.class);

Here the concrete classes implement IParsable . Would I be able to remove the runtime assert check and put a compile time check to see if 'IParsable' and call parse?

Also I was wondering if there is a way to enforce IFoo<-->Foo implements relationship at compile time in getItem() method and remove the typecasting (T) ?

EDIT: I thought I would give an outline for IFoo and Foo

public interface IFoo
{
    String getFooName();
    int getFooId();
    ....
}


class Foo implements IFoo, IParsable
{
      ...
}

EDIT2:

I just thought of a refactoring like this, now the only thing that's not a compile time check is the relation between interface and implementation.

  public static <U extends IParsable, T> T getItem(Element element, Class<T> clazz)
   {
         U item = null;
         if (clazz == IFoo.class)
         {
              item =  (U) new Foo();
         }
         else if (clazz == IBar.class)
         {
              item =  (U) new Bar();
         }
         ...

       item.parse(element)

       return (T) item;
   }

Maybe something like this:

public static <T> T getItem(Element element, Class<T> clazz) {
    IParsable item = null;
    if (clazz == IFoo.class) {
        item = new Foo();
    } else if (clazz == IBar.class) {
        item = new Bar();
    }
    item.parse();
    return (T) item;
}

The fact that Foo is an IParsable is checked at compile time. The other cast (to (T) ) is still a runtime exercise - but it was also the case in your example.

Here the concrete classes implement IParsable . Would I be able to remove the runtime assert check and put a compile time check to see if 'IParsable' and call parse?

From what I know - no, it's not possible. You will be passing object to this method at runtime, the compiler has no idea what objects will be passed. So compile time is not an option here. 

Also I was wondering if there is a way to enforce IFoo<-->Foo implements relationship at compile time in getItem() method and remove the typecasting (T) ?

Again, only at runtime with isAssignableFrom

you should register all the mappings at class setup time like this (it's ugly cause you can't check more than one bounds with the same type parameter):

private static Map<Class<?>, Class<?>> MAPPINGS = ...;

static {
  registerMapping(IFoo.class, Foo.class, Foo.class);
  // ...
}

private static <IT, TT extends IT, TP extends IParseable> void registerMapping(Class<IT> ifaceClazz, Class<TT> implClazz, TP parseableClazz) {
  MAPPINGS.put(ifaceClazz, implClazz);
}

then in getItem() your if block turns into a simple map lookup. note, this will not get rid of the (T) cast, but there's nothing you can do about that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM