Pregunta

I have the following 2 interfaces accordingly to abstract factory pattern:

public interface GenericObjectInterface<T extends Number>{
    public T getResult();
}
public interface AbstractFactoryInterface{
    public <T extends Number> GenericObjectInterface<T> createGenericObject();
}

I have an abstract class implementing GenericObject, but it's still unaware of the concrete type (it does only generic operations on Number):

public abstract class GenericAbstractClass<T extends Number> implements GenericObjectInterface<T>{   } 

Then I have a series of concrete class extending that perform generic parameter substitution:

public class IntegerObject extends GenericAbstractClass<Integer>{
     public Integer getResult(){}
}
....

Now, from inside an implementation of the factory I build the concrete type, that's implementing GenericObjectInterface but has lost it's generic parameter:

public class ConcreteFactory{
    public <T extends Number> GenericObjectInterface<T> greateGenericObject(Class<T> c){
         if (c.class.isInstance(Integer.class)){
             IntegerObject obj = new IntegerObject();
             //I would like to return obj
             GenericObjectInterface<T> a = new IntegerObject(); //errror
             GenericAbstractClass<T> a = new IntegerObject(); //errror

             return a;
          }else if (c.class.isInstance(Double.class)){
          }
    }
}

I would like to return obj that implements GenericObjectInterface but I don't know how can I do it. how can I solve this?

I'm used to abstract factory but I've never used it with generics. Am I doing some mistakes in interpreting the pattern?

¿Fue útil?

Solución

If your method returns an IntegerObject why don't you just return GenericObjectInterface<Integer>? You already know the parameter type.

In that case, just add a generic parameter to AbstractFactoryInterface, too:

public interface AbstractFactoryInterface<T extends Number> { ... }

public class ConcreteFactory implements AbstractFactoryInterface<Integer> { ... }

In your implementation the type of T would be inferred from the assignment, and thus you could do this:

 GenericObjectInterface<Double> g = new ConcreteFactory().greateGenericObject();

In that case T would be Double but you'd use Integer internally, resulting in this:

GenericObjectInterface<Double> a = new IntegerCell(); 

Since the compiler can't ensure that T will always be of type Integer it won't allow you to do that assignment.

Otros consejos

Abstract factory is characterized by the factory method returning an interface or abstract class reference instead of the concrete reference. It does not extend to type parameters.

Think of it this way: should you be able to do this?

public class ConcreteListFactory {
    public <T> List<T> createList() {
        return new ArrayList<String>();
    }
}

What if the caller wanted a List<Integer>?

If you want your factory to return a generified type, you should have your concrete class accept the type parameter. Otherwise have your factory method return a GenericObjectInterface<Integer>.

Alternatively, you could have your method accept a type token (Integer.class). For example:

public <T extends Number> GenericObjectInterface<T> createGenericObject(Class<T> clazz) {
    if ( clazz.equals(Integer.class) ) {
        return (GenericObjectInterface<T>) new IntegerObject();
    }
} 

This will result in an unchecked cast warning but you can prove to yourself that it is safe, and thus suppress the warning or ignore it.

Generally, factories are not implemented as generics because you can't examine the type of the generic to determine the type of object to create (you can't do T.getClass) which is why @Mark's example causes the class to be passed in as an argument.

I think, more usually you would have multiple concrete factories. One for each Number type that you intend to support.

public interface AbstractFactoryInterface<T extends Number> {
    public GenericObjectInterface<T> createGenericObject();
}


class IntegerFactory implements AbstractFactoryInterface<Integer>...
class LongFactory implements AbstractFactoryInterface<Long>...

You could then create a Map<Class, AbstractFactoryInterface>...

Map<Class, AbstractFactoryInterface> myMap = ...;
myMap.put(Integer.class, new IntegerFactory());
myMap.put(Long.class, new LongFactory ());

casting is perfectly fine here. if c==Integer.class, then T=Integer, casting GOI<Object> to GOI<T> is absolutely correct. It is a checked cast because you have checked that T=Integer before casting, therefore the unchecked warning can be legitimately suppressed.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top