Возвращение подкласса объектов с обобщениями - PullRequest
14 голосов
/ 20 июля 2010

С помощью абстрактного класса я хочу определить метод, который возвращает «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 с сообщением «Не торт!»

Ответы [ 4 ]

20 голосов
/ 20 июля 2010
public abstract class Foo<T extends Foo<T>>  // see ColinD's comment
{
    public T eat(String eatCake) 
    {
        return (T)this;
    }
}

public class CakeEater extends Foo<CakeEater> 
{
    public void f(){}
}

Редактировать

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

Другое предлагаемое решение с ковариантным типом возвращаемого значения должно делать то же самое - запрашивать у реализаторов подкласса на простом английском языке тип this. Это требование нельзя определить статической типизацией.

17 голосов
/ 20 июля 2010

Изящный подход с точки зрения клиента (обычно это тот, который вы хотите использовать) заключается в использовании ковариантных типов возврата, которые были добавлены для поддержки обобщений, как указывает Майкл Баркер.

Немного менее со вкусом, но более со вкусом, чем если бы актеры добавили getThis метод:

protected abstract T getThis();

public <T extends Foo> T eat(String eatCake) {
    ...
    return getThis();
}
5 голосов
/ 20 июля 2010

Я не думаю, что вам нужны дженерики. 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;
    }
}
0 голосов
/ 20 июля 2010

Подход, который я использовал ранее для достижения аналогичного поведения, заключается в том, чтобы подкласс передавал свой тип в конструктор (обобщенного) родительского типа. В качестве отказа от ответственности я генерировал подклассы на лету, и наследование было чем-то вроде хитрости, чтобы сохранить простоту генерации моего кода, как всегда, мой первый инстинкт - попытаться полностью удалить отношение extends.

...