简体   繁体   中英

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");
    }
}

This cannot be done by java. You can accomplish this by writing a "MetaFactory" (ie 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.

In Java, generic types are only intended for compile-time type checking, and are generally not available at runtime. This means that you can't make any runtime decisions based on T in your create -method.

You also can't just make the jump from knowing that you want a RequestFactory<String> to knowing that "the" concrete implementation for this purpose is the StringFactory . There could be other classes which also extend RequestFactory<String> , so you will have to explicitly state somewhere that StringFactory is what you want. If this was C++, you could write template specializations to say "this is the implementation for a RequestFactory<String> ", but this is not possible with generics.

Brett Okken already pointed out one possible alternative solution which is very similar to what I had written, so I'll just point to his answer for 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