Search code examples
javagenericsfactorydispatch

Dynamic dispatch and generic interface factories


How should I implement the RequestFactory interface in Factory such that I can create either StringRequest or IntRequest depending on what type is passed on?

In essence I want to create instances of concrete classes of a parametric abstract class dynamically, is this possible in Java?

public class Main {

    public static void main(String[] args) {
        Integer mInt = 10;
        String mString = "string";

        MyRequest mReq1 = new Factory<>(mString).getRequest();
        mReq1.PerformMyRequest();

        MyRequest mReq2 = new Factory<>(mInt).getRequest();
        mReq2.PerformMyRequest();
    }
}

class Factory<T> implements RequestFactory<T> {

    private final MyRequest<T> Req;

    public Factory(T body) {
        Req = create(body);
    }

    public MyRequest<T> getRequest() {
        return Req;
    }

    @Override
    // How do I implement the interface here so
    // that correct factory is invoked depending on T
    // so that I can return either StringRequest or IntRequest
    public MyRequest<T> create(T body) {
        return null;
    }

}

Factory Interface:

// Interface
interface RequestFactory<T> {
    MyRequest<T> create(T body);
}
// Concrete specialized factories
class StringFactory implements RequestFactory<String> {
    @Override
    public StringRequest create(String body) {
        return new StringRequest(body);
    }
}
class IntFactory implements RequestFactory<Integer> {
    @Override
    public IntRequest create(Integer body) {
        return new IntRequest(body);
    }
}

Generic abstract class with its two concrete children

// ======================================================

// AbstractClass
abstract class MyRequest<T> {
    T mVal;

    MyRequest(T body) {
        mVal = body;
    }

    public void PerformMyRequest() {
        System.out.println("-> From abstract: " + mVal);

    }

}
// Concrete classes that I'd like to automatically
// create using the factory above
class StringRequest extends MyRequest<String> {

    StringRequest(String body) {
        super(body);
    }

    public void PerformMyRequest() {
        super.PerformMyRequest();
        System.out.println("  -> From StringRequest");
    }
}
class IntRequest extends MyRequest<Integer> {

    IntRequest(Integer body) {
        super(body);
    }

    public void PerformMyRequest() {
        super.PerformMyRequest();
        System.out.println("  -> From IntRequest");
    }
}

Solution

  • This cannot be done by java. You can accomplish this by writing a "MetaFactory" (i.e. a factory of factories) which does type checking to choose the factory implementation to create and return.

    public final class RequestMetaFactory {
    
      public RequestFactory<T> newFactory(T req) {
        if (req instanceof String) {
          return new StringRequestFactory((String)req);
        }
    
        if (req instanceof Integer) {
          return new IntegerRequestFactory((Integer)req);
        }
    
        throw new IllegalArgumentException(req.getClass() + " not a supported arg type");
      }
    
    }
    

    This could be made more sophisticated by doing an SPI lookup to find actual RequestFactory instances and asking each what type they support.