2013-08-22 21 views
8

Mam do czynienia z problemem opisanym w this question, ale chciałbym znaleźć rozwiązanie (jeśli to możliwe) bez wszystkich rzutów i adnotacji @SuppressWarning.Metoda odziedziczona zwracana typ odniesienia

Lepszym rozwiązaniem byłaby taka, która opiera się na odwołanie jednego przez:

  • usuwania @SuppressWarning
  • usuwanie odlewane

Rozwiązania przedstawione tutaj będą oceniane z 2 punktów oparty na kryteria. Bounty przechodzi do rozwiązania z większością punktów lub "najbardziej elegancką", jeśli jest więcej niż jedna z 2 punktami.

+0

Doda nagrodę w 2 dni ze względu na mechanizm więc dla oferowanie nagród. Możesz teraz opublikować swoją odpowiedź. – pnt

+0

related: [Czy istnieje sposób, aby odnieść się do bieżącego typu ze zmienną typu?] (Http://stackoverflow.com/questions/7354740/is-there-a-way-to-refer-to-the-current -type-with-a-type-zmienna) –

Odpowiedz

9

Nie odlew, bez @SuppressWarning, kilka linii tylko:

public abstract class SuperClass<T extends SuperClass<T>> { 
    protected T that; 
    public T chain() { 
     return that; 
    } 
} 

public class SubClass1 extends SuperClass<SubClass1> { 
    public SubClass1() { 
     that = this; 
    } 
} 

public class SubClass2 extends SuperClass<SubClass2> { 
    public SubClass2() { 
     that = this; 
    } 
} 
+1

Zostało to wybrane jako najlepsza odpowiedź, ponieważ unika całkowicie rzutów, nie ma @SuppresWarnings i jest, według mnie, najbardziej elegancka, ponieważ pozwala również uniknąć nadpisania metody (odniesienie, które musisz "przesłonić" wygląda na czystsze i wymaga mniej kodu, jest też ukryty przed użytkownikiem, że w ogóle cokolwiek przesłonisz). Nagradza nagrodę w 23 godziny. – pnt

+0

@pnt Jest to w zasadzie to samo rozwiązanie, co zaakceptowana odpowiedź w zadanym pytaniu. Dlaczego ci się to nie podobało i zaakceptowałeś tutaj? –

+1

@FrancoisBourgeois Właściwie to nie jest to samo rozwiązanie. W drugim rozwiązaniu z 'return (T) this;' w klasie abstrakcyjnej, kompilator nie może wiedzieć w czasie kompilacji, czy rzut jest bezpieczny (nawet jeśli może być, tylko programista wie), a zatem potrzeba adnotacja @SuppressWarning. W moim rozwiązaniu nie ma potrzeby rzutowania, ponieważ 'that' jest ustawiony bezpośrednio w podklasie. A to * rozwiązanie bez obsady * jest dokładnie tym, o co tutaj proszono. – sp00m

1

Jednym z rozwiązań jest zastąpienie metody w klasie potomnej i zmiana typu powrotu na bardziej konkretny, np. typ dziecka. To wymaga rzucenia. Zamiast korzystać z typową (Child) obsadę, należy użyć metody Class#cast(Object)

public class Parent { 
    public Parent example() { 
     System.out.println(this.getClass().getCanonicalName()); 
     return this; 
    } 
} 

public class Child extends Parent { 
    public Child example() { 
     return Child.class.cast(super.example()); 
    } 

    public Child method() { 
     return this; 
    } 
} 

W obsadzie jest ukryty w standardowej metody. Ze źródła Class.

public T cast(Object obj) { 
    if (obj != null && !isInstance(obj)) 
     throw new ClassCastException(cannotCastMsg(obj)); 
    return (T) obj; 
} 
+0

Naprawdę nie jest rozwiązaniem, ponieważ nie usuwa rzutowania (tylko zawija je i ukrywa) i nie rozwiązuje pierwotnego problemu w zadanym pytaniu. Tak, oczywiście możesz zastąpić metodę, aby zwrócić bardziej konkretny typ, ale cały punkt ma na celu ponowne jej użycie i nie musisz go zastępować w każdym potomku. – pnt

+0

@pn Problem oznaczony jako _I chcę to zaimplementować bez użycia dodatkowych interfejsów i dodatkowych metod, i używać go bez rzutów klasowych, argumentów pomocniczych itd ._ Rzecz polega na tym, że nie możesz. Proponowane rozwiązania mają albo dodatkowe metody i/lub dodatkowe argumenty. Albo zastąpisz daną metodę, albo wprowadzisz inną metodę do rodzica i zastąpisz ją. W rozwiązaniu Generics należy również wprowadzić parametr typu. –

5

Jednym ze sposobów jest zdefiniowanie metody abstrakcyjne getThis() w Parent klasie, i uczynić wszystkie Child klasy zastępują go, wracając odniesienie this. Jest to sposób na odzyskanie typu obiektu this w hierarchii klas.

kod wyglądałby następująco:

abstract class Parent<T extends Parent<T>> { 

    protected abstract T getThis(); 

    public T example() { 
     System.out.println(this.getClass().getCanonicalName()); 
     return getThis();   
    } 
} 

class ChildA extends Parent<ChildA> { 

    @Override 
    protected ChildA getThis() { 
     return this; 
    } 

    public ChildA childAMethod() { 
     System.out.println(this.getClass().getCanonicalName()); 
     return this; 
    } 
} 

class ChildB extends Parent<ChildB> { 

    @Override 
    protected ChildB getThis() { 
     return this; 
    } 

    public ChildB childBMethod() { 
     return this; 
    } 
} 


public class Main { 

    public static void main(String[] args) throws NoSuchMethodException { 
     ChildA childA = new ChildA(); 
     ChildB childB = new ChildB(); 

     childA.example().childAMethod().example(); 
     childB.example().childBMethod().example(); 
    } 
} 

jak na wymagania, nie ma Casting i nie @SuppressWarnings. Nauczyłem się tej sztuczki kilka dni wstecz od Angelika Langer - Java Generics FAQs.

referencyjny:

+1

Możesz chcieć zadeklarować, że 'Parent >'. W przeciwnym razie można by "rozszerzyć Parent ", a metoda łączenia zakończyłaby się na 'String getThis()'. –

+0

@SotiriosDelimanolis. Ach! Dobrze. Dzięki :) –

Powiązane problemy