2010-07-19 14 views
11

z klasy abstrakcyjnej Chcę zdefiniować metodę, która zwraca „to” dla podklasy:Zwracanie obiektów podklasy z rodzajowych

public abstract class Foo { 
    ... 
    public <T extends Foo> T eat(String eatCake) { 
     ... 
     return this; 
    } 
} 

public class CakeEater extends Foo {} 

Chcę być w stanie robić takie rzeczy jak:

CakeEater phil = new CakeEater(); 
phil.eat("wacky cake").eat("chocolate cake").eat("banana bread"); 

Prawdopodobnie chleb bananowy rzuciłby wyjątek IllegalArgumentException z komunikatem "Not a cake!"

Odpowiedz

13
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(){} 
} 

Edytuj

Nie ma problemu, aby wymagać podklasy zachowywać się w w pewien sposób wykracza poza to, co może sprawdzić statyczne pisanie. Robimy to cały czas - strony i strony zwykłego angielskiego, aby określić sposób pisania podklasy.

Inne proponowane rozwiązanie, z kowariantnym typem zwracania, musi działać tak samo - żądając wywołań podklas, w prostym języku angielskim, aby zwrócić typ this. Tego wymogu nie można określić za pomocą typowania statycznego.

+1

Powinien być 'Foo >'. To działa, chociaż możesz łatwo stworzyć klasę, która wysadzi się w "Wyjątek ClassCastException", gdy nazwiesz 'eat', na przykład' Bar klasy rozszerza Foo '. – ColinD

+0

To jest rozwiązanie, którego użyłem, ale zauważysz, że powrót ma niezaznaczone ostrzeżenie o obsadzie. Logicznie rzecz biorąc, sensowne jest, że możemy rzutować na T, ponieważ jest to podklasa T. – Sarabjot

+2

Tak.W lepszym świecie Java mogłaby mieć typ 'This', który byłby bardzo przydatny w wielu przypadkach. – irreputable

5

Nie sądzę, trzeba rodzajowych Java 5 (lub nowszy) ma kowariantna rodzajów powrotne, np:

public abstract class Foo { 
    ... 
    public Foo eat(String eatCake) { 
     ... 
     return this; 
    } 
} 

public class CakeEater extends Foo { 

    public CakeEater eat(String eatCake) { 
     return this; 
    } 
} 
+0

Jeśli jest w pełni zdefiniowany w nadklasie, tworzy nadmiarowy kod, aby zdefiniować go w podklasie; Może nazwać superklasę, ale wydaje się to niepotrzebne. Działa, jeśli rzucisz to jako typ T i pominiesz ostrzeżenia. – Sarabjot

+0

Działa, jeśli rzucisz to jako typ T i pominiesz ostrzeżenia. Powodem należy zwrócić typ podklasy jest, ponieważ: methodTakesCakeEater ((nowy CakeEater) .eat ("ciasto") jeść ("pie"); methodTakesCakeEater potrzebuje parametru CakeEater – Sarabjot

+5

przypadku.. rozwiązanie zawiera '@ SuppressWarnings' to nie powinno być uważane za rozwiązanie .. Tylko moje dwa centy – whiskeysierra

15

Smakowite podejście z punktu widzenia klienta (który zwykle jest tym, który chcesz podjąć) polega na użyciu typów zmiennych kowariantnych, które zostały dodane do obsługi generycznych, jak zauważa Michael Barker.

Nieco mniej smaczne, ale bardziej gustowny, że obsada jest dodanie getThis metody:

protected abstract T getThis(); 

public <T extends Foo> T eat(String eatCake) { 
    ... 
    return getThis(); 
} 
0

Podejście Użyłem przed osiągnąć podobne zachowanie jest mieć podklasa przekazać jej wpisać do konstruktor (generowanego) typu nadrzędnego. Na zasadzie disclaimer generowałem podklasy w locie, a dziedziczenie było trochę oszustwem, aby moje generowanie kodu było proste, ponieważ zawsze moim pierwszym instynktem jest próba całkowitego zniesienia relacji.