Muneer Y
Muneer Y

Reputation: 83

Java Generic Interfaces with multiple implementations

I have the following scenario:

public abstract class BaseTask{...}

public class TaskA extends BaseTask {....}

public class TaskB extends BaseTask {....}

public interface TaskService<T extends BaseTask>{
        void process(T task);
}

@Service @Qualifier("taskServiceA")
public class TaskServiceA<TaskA> implements TaskService<TaskA>{
}

@Service @Qualifier("taskServiceB")
public class TaskServiceB<TaskB> implements TaskService<TaskB>{
}

public class ProcessingService{

        @Autowired @Qualifier("taskServiceA")
        private TaskService<TaskA> taskAService;

        @Autowired @Qualifier("taskServiceB")
        private TaskService<TaskB> taskBService;

        public void process(Order o){
            BaseTask task = o.getTask();
            getTaskService(o).start(task);
        }

        private <T extends BaseTask> TaskService<T> getTaskService(Order o){
            if("atype".equals(o.type)){
              return (TaskService<T>) taskAService;
            } else if("btype".equals(o.type)){
              return (TaskService<T>) taskBService;
            }
        }
}

Update: I have reworded the question because the answers I was getting was not what I was looking for.

My questions is related to the getTaskService method.

  1. Why do I need to cast the return value like this

    return (TaskService) taskAService;

  2. Is there another way to implement the getTaskService() method without having to do the cast?

I will really appreciate if someone can provide some explanation or better implementation for the getTaskService method.

Upvotes: 4

Views: 137

Answers (2)

Maurice Perry
Maurice Perry

Reputation: 9650

Because type T is resolved wherever the method is used. The following statement is valid:

    TaskService<TaskA> s = getTaskService(o);

So is:

    TaskService<TaskB> s = getTaskService(o);

So within the method getTaskService, you don't know much about T.

The correct way to do this would be:

private TaskService<? extends BaseTask> getTaskService(Order o) {
    if ("atype".equals(o.type)) {
        return taskAService;
    } else if ("btype".equals(o.type)) {
        return taskBService;
    } else {
        return null;
    }
}

The assignment above would have to become:

TaskService<? extends BaseTask> s = getTaskService(o);

Upvotes: 1

Kishore Bandi
Kishore Bandi

Reputation: 5701

How about this?

  • No need of any if conditions.
  • Later if someone does add another implementation of BaseTask they don't have to change any other code.
  • Also I recommend changing "atype" to Enum and using Map<EnumTask, ? extends BaseTask> serviceMap; instead of String.

Your final invocation of Tasks can be without any checks

@Service
class ProcessingService { 

        @Autowired 
        private TaskServiceManager taskServiceManager;

        public void process(Order o){
            taskServiceManager.getServiceTask(o.type).start(task);
        }
}

Other classes

enum ServiceEnum {
    TaskA,
    TaskB
}

public class TaskA extends BaseTask {....}
public class TaskB extends BaseTask {....}

public abstract class TaskService<T extends BaseTask>{

    public TaskService(ServiceEnum serviceEnum, TaskServiceManager taskServiceManager) {
        taskServiceManager.registerTask(serviceEnum, this);
    }
    void process(T task);
}

@Service @Qualifier("taskServiceA")
public class TaskServiceA<TaskA> implements TaskService<TaskA>{
        @Autowired 
        public TaskA(TaskServiceManager taskServiceManager) {
              super(ServiceEnum.TaskA, taskServiceManager);
        }
}

@Service @Qualifier("taskServiceB")
public class TaskServiceB<TaskB> implements TaskService<TaskB>{...}

@Service
class  TaskServiceManager {
    Map<ServiceEnum, ? extends TaskService> serviceMap;

    public <T extends TaskService> void registerTask(ServiceEnum serviceName, T task) {
        if(serviceMap.containsKey(serviceName)) {
            throw new IllegalArgumentException("ServiceName is already in the Map");
        }
        serviceMap.put(serviceName, task);
    }

    public <T extends TaskService> T getServiceTask(ServiceEnum serviceName) {
        if(!serviceMap.containsKey(serviceName)) {
            throw new IllegalArgumentException("ServiceName is not Registered");
        }
        return serviceMap.get(serviceName);
    }
}

Upvotes: 1

Related Questions