Возврат подкласса объектов с помощью дженериков
С абстрактным классом я хочу определить метод, который возвращает "this" для подклассов:
public abstract class Foo {
...
public <T extends Foo> T eat(String eatCake) {
...
return this;
}
}
public class CakeEater extends Foo {}
Я хочу иметь возможность делать такие вещи, как:
CakeEater phil = new CakeEater();
phil.eat("wacky cake").eat("chocolate cake").eat("banana bread");
Возможно, банановый хлеб вызовет IllegalArgumentException с сообщением "Не торт!"
Ответы
Ответ 1
public abstract class Foo<T extends Foo<T>> // see ColinD comment
{
public T eat(String eatCake)
{
return (T)this;
}
}
public class CakeEater extends Foo<CakeEater>
{
public void f(){}
}
Изменить
Нет никаких проблем, чтобы требовать, чтобы подкласс вел себя определенным образом, кроме того, что может проверить статическая типизация. Мы делаем это все время - страницы и страницы простого английского языка, чтобы указать, как вы пишете подкласс.
Другое предлагаемое решение с ковариантным типом возврата должно сделать то же самое - попросить разработчиков подкласса на простом английском языке вернуть тип this
. Это требование не может быть задано путем статического ввода.
Ответ 2
Со вкусом подход с точки зрения клиента (который обычно является тем, который вы хотите принять) заключается в использовании ковариантных типов возврата, которые были добавлены для поддержки дженериков, как указывает Майкл Баркер.
Немного менее со вкусом, но более со вкусом, чтобы бросить добавить метод getThis
:
protected abstract T getThis();
public <T extends Foo> T eat(String eatCake) {
...
return getThis();
}
Ответ 3
Я не думаю, что вам нужны общие шаблоны. Java 5 (и более поздние версии) имеет ковариантные типы возврата, например:
public abstract class Foo {
...
public Foo eat(String eatCake) {
...
return this;
}
}
public class CakeEater extends Foo {
public CakeEater eat(String eatCake) {
return this;
}
}
Ответ 4
Подход, который я использовал ранее для достижения аналогичного поведения, заключается в том, чтобы подкласс передал свой тип в конструктор (генерализованного) родительского типа. В качестве отказа от ответственности я создавал подклассы "на лету", и наследование было чем-то вроде обмана, чтобы сохранить мое поколение кода простым, так как всегда мой первый инстинкт - попытаться полностью удалить отношения extends.