Обязательный сервис generi c с его реализацией и подтипами - PullRequest
0 голосов
/ 31 января 2020

Отказ от ответственности: принесите мне достаточно кода для объяснения сценария.

В одном из модулей maven (ядро) у нас есть следующие классы:

abstract class ReportingEvent {
}

abstract class Element {

    public <E extends ReportingEvent> Optional<E> getReportEvent() {
        return Optional.empty();
    }

}

такие сервисы, как:

public interface Reporting<E extends ReportingEvent> {
    void report(E event);
}

interface InternalService {
}

public class InternalServiceImpl implements InternalService {

    @Inject
    Reporting<ReportingEvent> reporting; // 1. Possible to use a generic type? How?

    private void reportEvents(BatchRequest batchRequest) {
        batchRequest.stream()
                // section below is of importance
                .map(m -> m.getEntity().getReportEvent()) // the generic method from 'Element'
                .filter(Optional::isPresent)
                .map(Optional::get)
                .forEach(event -> reporting.report(event)); // method from 'Reporting'
    }
}

class CoreBindingModule extends AbstractModule {
    protected void configure() {
        bind(InternalService.class).to(InternalServiceImpl.class).in(Singleton.class);
    }
}

Далее, в другом модуле maven (потребителе), который мы развертываем, у нас есть классы, связанные с вышеописанным и реализующие его как:

abstract class BaseReporting extends ReportingEvent {
}

class ColdReporting extends BaseReporting {
}

abstract class Node extends Element {
}

class Cold extends Node {
    @Override
    public Optional<ColdReporting> getReportEvent() {
        return Optional.ofNullable(new ColdReporting()); // some business logic
    }
}

class ReportingImpl implements Reporting<ReportingEvent> { // 2. Use 'BaseReporting' here
    void report(ReportingEvent event){}
}

class ConsumerBindingModule extends AbstractModule {
    protected void configure() {
        bind(new TypeLiteral<Reporting<ReportingEvent>>() {}).to(ReportingImpl.class).in(Singleton.class);
    }
}

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

A ... Поэтому, если я изменяю привязку в модуле-потребителе на

bind(new TypeLiteral<Reporting<BaseReporting>>() {}).to(ReportingImpl.class).in(Singleton.class);

с

class ReportingImpl implements Reporting<BaseReporting> {
    void report(BaseReporting event){}
}

, я получаю ошибку

No implementation for Reporting<ReportEvent> was bound.
while locating Reporting<ReportEvent> for field at InternalServiceImpl.reporting(InternalServiceImpl.java:21)

, что актуально, и я все равно не могу использовать Reporting<BaseReporting> в основном модуле.

B ... Вкл с другой стороны, если я попытаюсь ввести Reporting как:

@Inject
Reporting<? extends ReportingEvent> reporting;

, тогда IDEA сообщит

 Required type: capture of ? 
 Provided: ReportingEvent

в строке

...forEach(event -> reporting.report(event))

Есть ли способ обойти эту ситуацию, пытаясь решить для 1 и 2 , как указано в разделах кода?

1 Ответ

0 голосов
/ 06 февраля 2020

(я, возможно, восстанавливаю то, что вы уже знаете по большей части)

Если оставить в стороне ваши модули и конфигурацию Guice, ваша проблема может быть записана как

Reporting<ReportingEvent> reportingEvent = new ReportingImpl();
Reporting<? extends ReportingEvent> reporting = baseReporting;
reporting.report(reportingEvent); //won't work

, что идентичный

List<? extends String> list = new ArrayList<>();
list.add("a"); //won't work

Скажем, у вас есть класс HotReporting, который расширяет BaseReporting, таким образом, у вас есть что-то вроде этого

public class ReportingImpl implements Reporting<ReportingEvent> { 
    public void report(ReportingEvent event){}
}
public class ColdReportingImpl implements Reporting<ColdReporting> { 
    public void report(ColdReporting event){}
}

public class HotReportingImpl implements Reporting<HotReporting> { 
    public void report(HotReporting event){}
}

Предположим, если вы ввели HotReportingImpl для поля Reporting<? extends ReportingEvent> reporting.

В вашем коде, что если m.getEntity().getReportEvent() вернет ColdReporting? Он не совместим с тем, что ожидает метод report (HotReporting).


Вариант 1:

Я бы попытался избавиться обобщенных параметров типа c и определите Reporting как

public interface Reporting {
    void report(ReportingEvent event);
}

public class BaseReportingImpl implements Reporting {
    @Override
    public void report(ReportingEvent event) {

    }
}
//... and so on

Проблемы:

  • Если вы полагались на точный подтип ReportingEvent в реализации (нужны типы, которые не годятся).
  • Тем не менее, HotReportingImpl может получить объект ColdReporting в качестве аргумента.

Option 2:

Вы можете создать InternalService generi c, добавив в него параметры типа.


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

Посмотрите на Typesafe разнородный контейнеры Джошуа Блоха . Ваша проблема совпадает с этим. Вы можете сохранить соответствие типа ReportingEvent соответствующему подклассу Reporting с этим аргументом типа.

...