Аргументы общего типа, которые конкретно расширяющий класс? - PullRequest
4 голосов
/ 30 сентября 2008

Я хочу иметь класс, который реализует интерфейс, который указывает конкретный подкласс в качестве параметра.

public abstract Task implements TaskStatus<Task> {
  TaskStatus<T> listener;

  protected complete() {
      // ugly, unsafe cast
      callback.complete((T) this);
  }
}

public interface TaskStatus<T> {
   public void complete(T task);
}

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

Итак, лучшее, что я придумал, это:

public abstract Task<T extends Task> implements TaskStatus<T> {
}

Вы бы расширили это, написав:

public class MyTask extends Task<MyTask> {
}

Но это также будет справедливо:

public class MyTask extends Task<SomeOtherTask> {
}

И вызов обратного вызова будет взорван ClassCastException. Итак, этот подход просто ошибочен и сломан, или есть верный способ сделать это, я как-то упустил?

Ответы [ 4 ]

2 голосов
/ 30 сентября 2008

Непонятно, что вы пытаетесь сделать внутри Task. Однако, если вы определите универсальный класс Task<T> следующим образом:

class Task<T extends Task<T>> { ... }

Возможны следующие два:

class MyTask extends Task<MyTask> { ... }
class YourTask extends Task<MyTask> { ... }

Но запрещено следующее:

class MyTask extends Task<String> { ... }

В приведенном выше определении Task используется F-ограниченный полиморфизм, довольно продвинутая функция. Вы можете проверить исследовательскую работу " F-ограниченный полиморфизм для объектно-ориентированного программирования " для получения дополнительной информации.

1 голос
/ 30 сентября 2008

Я предлагаю добавить getThis, который должен возвращать этот тип соответственно. Конечно, подкласс может плохо себя вести, но это всегда так. Чего вы избегаете, так это приведения и возможности ClassCastException.

public abstract class Task<THIS extends Task<THIS>> {
    private TaskStatus<THIS> callback;

    public void setCallback(TaskStatus<THIS> callback) {
        this.callback = callback==null ? NullCallback.INSTANCE : callback;
    }

    protected void complete() {
        // ugly, unsafe cast
        callback.complete(getThis());
    }

    protected abstract THIS getThis();
}

public interface TaskStatus<T/* extends Task<T>*/> {
    void complete(T task);
}

public class MyTask extends Task<MyTask> {
    @Override protected MyTask getThis() {
        return this;
    }
}

Эта проблема часто возникает у строителей.

0 голосов
/ 30 сентября 2008

Я действительно могу видеть, как это работает, только если вы в конструкторе используете тип arg. С помощью. introspection и Class.getSuperType () вы можете проверить тип аргументов и убедиться, что тип аргумент соответствует этому классу.

Что-то вроде:

assert getClass() == ((ParameterizedType) getSuperType()).getTypeArguments()[0];

(Это сверху моей головы, проверьте JavaDocs для проверки).

Я не уверен, где создается обратный вызов, хотя в вашем коде. Вы пропустили его объявление в верхней части.

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

0 голосов
/ 30 сентября 2008

У меня небольшие проблемы с пониманием того, чего вы пытаетесь достичь через это. Не могли бы вы предоставить более подробную информацию?

Мое чтение кода говорит о том, что у вас есть эти задачи, которые будут разделены на подклассы, и после того, как задачи будут выполнены, поток выполнения вызовет complete () для задачи. В этот момент вы хотите вызвать обратный вызов и передать ему объект подкласса. Я думаю, что это проблема. Вы пытаетесь поместить знания о потенциальных подклассах в свой абстрактный класс, который является нет-нет.

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

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