0

Lets say i have some generic interfaces

public interface Service<T> {
    T getData();
}

public interface ResponseBuilder<T> {
    void build(T response);
}

and some generic class which is using those interfaces

public class Orchestrator<T> {
    private Service<T> service;
    private List<ResponseBuilder<T>> responseBuilders;

    public Orchestrator(Service<T> serviceImpl, List<ResponseBuilder<T>> buildersImpl){
        this.service = serviceImpl;
        this.responseBuilders = buildersImpl; 
    }

    public void execute() {
        T response = service.getData();
        responseBuilders
            .stream()
            .map(builder -> builder.build(response))
            .forEach(data -> storageUtil.upload(data));
    }
}

when a client developer is going to use these APIs how can i enforce him/her to pass same type in the concrete implementations of these generic interfaces, so as to avoid type mismatch exception.

An instance created of Orchestrator without specifying its type can take different types as argument eg:-

public class App{
    public static void main(String[] args){
        ResponseBuilder<String> response1 = new SomeImpl(); // type STRING
        Service<Integer> service = new SomeServiceImpl(); // type INTEGER

        // completely valid and compilable code, will throw ex at runtime
        Orchestrator orch = new Orchestrator(service, Arrays.asList(response1));
   }
}

What can be a better design?

aatish rana
  • 149
  • 1
  • 15

2 Answers2

0

Disallow raw types.

    Orchestrator orch = new Orchestrator(service, Arrays.asList(response1));

That should produce a warning. Don't produce warnings.

    Orchestrator<T> orch = new Orchestrator<T>(service, Arrays.asList(response1));
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
0

I think what you are doing is correct but only missing the part of utilizing the generic functionality properly.

You are not using the type of Orchestrator T.

When you are accepting generic type instances of other objects then you can verify that they are of the same generic type with inheritance.

for example, In your class Orchestrator you should do something like this:

public class Orchestrator<T> {

    private Service<? extends T> service;
    private List<? extends  ResponseBuilder<? extends  T>> responseBuilders;

    public Orchestrator(Service<? extends T> service, List<? extends  ResponseBuilder<? extends  T>> buildersImpl){
        this.service = service;
        this.responseBuilders = buildersImpl;
    }
}

Mismatch types here would throw an error in compile time. This would work if the instance of Orchestrator is created with the type definations like

= new Orchestrator<String>(....);

further, if we have only a few types of generic possibilities then the best way would be to make Orchestrator abstract and the class that extends it should specify the type. for example,

public class StringOrchestrator extends Orchestrator<String> {

    public StringOrchestrator(Service<? extends String> service,
                              List<? extends ResponseBuilder<? extends String>> buildersImpl) {
        super(service, buildersImpl);
    }
}

Then, instantiate Orchestrator with some concrete implementation which is not generic in sense like:

Orchestrator<String> orchestrator = new StringOrchestrator(service,list);