Уйти от ошибки компиляции: reason: Incompatible equality constraint: T and?

Рейтинг: 1Ответов: 1Опубликовано: 20.07.2023

В приложении есть интерфейс обработчик с обобщенным типом (в теории - любой тип):

public interface Worker<T> {

    T action1();

    void action2(T object);

}

примеры реализации:

public class Worker1 implements Worker<String> {

    @Override
    public String action1() {
        return "1";
    }

    @Override
    public void action2(String object) {
        System.out.println("String: " + object.replace("1", "2"));
    }

}

public class Worker2 implements Worker<Integer> {

    @Override
    public Integer action1() {
        return 1;
    }

    @Override
    public void action2(Integer object) {
        System.out.println("Integer: " + (object + 1));
    }

}

а так же есть сервис, который может вызывать эти обработчики:

public class Runner {

    <T> void run(List<Worker<T>> workers) {
        for (Worker<T> worker : workers) {
            T object = worker.action1();
            worker.action2(object);
        }
    }

}

Вызов сервиса выглядит следующим образом:

List<Worker<?>> workers = Lists.newArrayList(new Worker1(), new Worker2());
new Runner().run(workers);

На строке:

new Runner().run(workers);

будет ошибка компиляции:

reason: Incompatible equality constraint: T and ?

что логично, т.к. в данном случае нужно определять коллекцию Worker не с wildcard, а с конкретным типом (java не может вывести тип).

Вопрос, каким образом можно переделать данный алгоритм, для того, чтобы:

  1. Сохранить типизацию в Worker, для того, чтобы в параметрах метода action2 можно было работать с конкретным типом без приведения типов (cast).
  2. Инициализировать список Worker'ов разного типа и передать их в сервис Runner без ошибок и без предупреждений вида: raw use parametrized.. (то есть не использовать raw типы).

Подходил к проблеме с разных сторон (шаблоны проектирования, изменение архитектуры и так далее), но пока решение не удалось найти, надеюсь на помощь сообщества.

Заранее спасибо.

Ответы

▲ 1Принят

Исправить ошибку компиляции можно следующим способом. Позвольте классу Runner принимать в методе run аргумент типа List<Worker<?>>, а не List<Worker<T>>. И вынесите запуск одного Worker в отдельный метод:

public class Runner {

    public void run(List<Worker<?>> workers) {
        for (Worker<?> worker : workers) {
            run1(worker);
        }
    }

    private <T> void run1(Worker<T> worker) {
        T object = worker.action1();
        worker.action2(object);
    }

}