Почему Java не может определить тип лямбда-выражения с помощью параметра общего типа? - PullRequest
0 голосов
/ 20 июня 2019

С учетом следующего кода:

abstract class Event {
}

class MyEvent extends Event {
}

interface EventSubscriber<T extends Event> {

  void onMessage(T message);

  Class<T> getMessageType();
}

interface MyEventSubscriber extends EventSubscriber<MyEvent> {

  @Override
  default Class<MyEvent> getMessageType() {
    return MyEvent.class;
  }
}

class SubscriberManager {

  public void subscribe(EventSubscriber<? extends Event> subscriber) {
  }
}

Я хотел бы получить доступ к параметру универсального типа, который удерживает подписчик события, путем вызова метода getMessageType. Я также хотел бы использовать SubscriberManager, передавая лямбда-выражения в метод subscribe:

subscriberManager.subscribe((MyEvent event) -> {});

К сожалению, компилятор Java не может определить тип лямбда-выражения, которое передается методу subscribe, хотя для меня совершенно очевидно, что тип лямбда-выражения может быть выведен из аргумента лямбды - MyEvent -> MyEventSubscriber. Компилятор Java выдает мне следующую ошибку:

несовместимые типы: EventSubscriber не является функциональным интерфейсом несколько не переопределяющих абстрактных методов, найденных в интерфейсе EventSubscriber

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

MyEventSubscriber myEventSubscriber = (MyEvent event) -> {};
subscriberManager.subscribe(myEventSubscriber);

subscriberManager.subscribe(new MyEventSubscriber() {
  @Override
  public void onMessage(MyEvent message) {

  }
});

Я мог бы добавить перегруженный метод в класс SubscriberManager, удалить метод getMessageType из интерфейса EventSubscriber (так как мы знали бы фактический тип подписчика и, следовательно, тип сообщения, который он содержит) и использовать простое лямбда-выражение, которое я упомянул в первом примере кода, но оно сделает весь код менее «полиморфным», я думаю:

class SubscriberManager {

  public void subscribe(EventSubscriber<? extends Event> subscriber) {
  }

  public void subscribe(MyEventSubscriber subscriber) {
  }
}

1 Ответ

0 голосов
/ 20 июня 2019

Проблема в том, что ваш EventSubscriber интерфейс не является функциональным интерфейсом, поскольку ошибка говорит вам: есть 2 метода для реализации там. Тот факт, что вы создали настоящий функциональный интерфейс с именем MyEventSubscriber, будет означать, что вы хотите, чтобы java каким-то образом интуитивно понял, что он существует.

Java не занимается поиском миллионов классов в пути к классам, просто чтобы посмотреть, есть ли что-нибудь во всем этом беспорядке, которое может работать или не работать. Надеюсь, если выразить это так, очевидно, почему это так и почему это никогда не сработает.

Конкретно: поскольку есть лямбда, java должна быть нацелена на его тип. Для этого java сначала проверяет контекст, окружающий лямбду: он проверяет различные методы с именем subscribe и замечает, что есть только один, и ему нужен аргумент типа EventSubscriber. Затем он назначает лямбда-тип для этого типа и сразу же завершается ошибкой, поскольку он не является функциональным интерфейсом. Компилятор не может понять, что он должен указывать на цель MyEventSubscriber.

Я немного поиграл с использованием отражения, чтобы проверить фактический тип, но это не работает; вам придется найти другой способ решения этой проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...