Абстрактный Java-класс со слушателем, который использует конкретный класс в своем обратном вызове - PullRequest
3 голосов
/ 13 мая 2019

Я хочу иметь абстрактный Java-класс, подобный следующему:

abstract class AbstractFoo<F extends AbstractFoo<F, L>, L extends FooListener<F, L>> {
  private final Class<L> listenerClass;
  protected AbstractFoo(Class<L> listenerClass) {
    this.listenerClass = listenerClass;
  }

  interface FooListener<F extends AbstractFoo<F, L>, L extends FooListener<F, L>> {
    void callback(F foo);
  }

  // Bar might implement FooListener, but I don't control it,
  // so I have no guarantee
  public void externalMethod(Bar bar) {
    if (listenerClass.isInstance(bar)) {
      L listener = listenerClass.cast(bar);

      listener.callback(this); // does not compile
    }
  }
}

listener.callback(this); не компилируется, потому что нет гарантии, что this того же типа, что и F.Можно ли как-то гарантировать, что F является супертипом this?

1 Ответ

1 голос
/ 13 мая 2019

То, что вы пытаетесь сделать, это эмулировать тип SELF в Java с использованием обобщений. См. эту ссылку или другой сайт , чтобы узнать, как это сделать. Тем не менее, нет способа принудительно установить, что F (или тип SELF) на самом деле является одним и тем же типом, например (см. Параметр типа ConcreteFoo2):

static class Bar implements FooListener<ConcreteFoo, Bar> {

    @Override
    public void callback(final ConcreteFoo foo) {
        // TODO Auto-generated method stub

    }

}

static class ConcreteFoo2 extends AbstractFoo<ConcreteFoo, Bar> {

    protected ConcreteFoo2(final Class<Bar> listenerClass) {
        super(listenerClass);
    }

}

static class ConcreteFoo extends AbstractFoo<ConcreteFoo, Bar> {

    protected ConcreteFoo(final Class<Bar> listenerClass) {
        super(listenerClass);
    }

}

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

  • Действительно ли слушатели должны знать конкретный класс?

  • Действительно ли AbstractFoo должен знать конкретную реализацию класса слушателя?

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


РЕДАКТИРОВАТЬ: одним из возможных решений, если вы не хотите приводить (F) this, было бы предоставить абстрактный метод protected abstract F getSelf();, который конкретные реализации реализуют, возвращая this.

См. Этот упрощенный код, например:

static final class Bar implements FooListener<ConcreteFoo> {

    @Override
    public void callback(final ConcreteFoo foo) {
        // TODO Auto-generated method stub

    }

}

static final class ConcreteFoo extends AbstractFoo<ConcreteFoo> {

    protected ConcreteFoo(final Class<? extends FooListener<ConcreteFoo>> listenerClass) {
        super(listenerClass);
    }

    @Override
    protected ConcreteFoo getSelf() {
        return this;
    }

}

static abstract interface FooListener<FOO extends AbstractFoo<FOO>> {

    void callback(FOO abstractFoo);
}

static abstract class AbstractFoo<SELF extends AbstractFoo<SELF>> {

    private final Class<? extends FooListener<SELF>> listenerClass;

    protected AbstractFoo(final Class<? extends FooListener<SELF>> listenerClass) {
        this.listenerClass = listenerClass;
    }

    protected abstract SELF getSelf();

    // Bar might implement FooListener, but I don't control it,
    // so I have no guarantee
    public void externalMethod(final Bar bar) {
        if (listenerClass.isInstance(bar)) {
            final FooListener<SELF> listener = listenerClass.cast(bar);

            listener.callback(getSelf()); // compiles
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...