2012-02-03 10 views
5

Spójrz na ten kod (od here)Dziedziczenie i przesyłanie: czy to jest dobra wersja Java?

abstract class EntityA { 
    AssocA myA; 
    abstract void meet(); 
} 

abstract class AssocA { 
    int something; 
    abstract void greet(); 
} 

class AssocAConcrete extends AssocA { 
    void greet() { 
     System.out.println("hello"); 
    } 
    void salute() { 
     System.out.println("I am saluting.") 
    } 
} 

class EntityAConcrete extends EntityA { 
    void meet() { 
     System.out.println("I am about to meet someone"); 
     ((AssocAConcrete)myA).salute(); 
    } 
} 

Istnieją dwa równoległe spadkowe drzewa, dla klasy dominującej i powiązanej klasy. Problem jest z linii 23:

((AssocAConcrete)myA).salute();

Jest to ból i mam coś takiego w całym moim kodu. Chociaż ta linia jest częścią konkretnej implementacji Entity, muszę przypomnieć, że chcę użyć konkretnej implementacji AssocA, Assocaconcrete.

Czy jest jakiś rodzaj adnotacji, aby zadeklarować ten związek? Czy istnieje lepszy, bardziej kolokwialny sposób Java do wyrażenia tego projektu? Dzięki!


Jest to odpowiedź na @Dave, ponieważ chcę, aby umieścić jakiś kod w ...

Ciekawe! Tak więc inwokacja wyglądałaby tak:

AssocAConcrete myAssoc = new Assoca(); 
EnitityA<T extends AssocA> myEntity = new EntityA<AssocAConcrete>(); 
myEntity.setAssoc(myAssoc); 
myAssoc.salute(); 

Tak? To jest naprawdę fajne. Myślę, że go użyję!

+0

Jeśli napiszesz 'AssocAConcrete myA;' zamiast 'AssocA myA;' (linia 2), rozwiąże to. Ale czy to złamie resztę kodu? –

+0

Tak, cała idea jest taka, że ​​mogą istnieć różne warianty AssocAConcrete, a klienci EntityA nie chcieliby być od tego zależni. Zauważ, że kod, który używa właśnie AssocA i EntityA nigdy nie zna ani nie wspomina o jednej lub drugiej klasie Concrete. I tylko dwie konkretne klasy "A" kiedykolwiek znają szczegóły implementacji. – pitosalas

Odpowiedz

8

Myślę, że jest to dużo neater wykorzystaniem generycznych ...

abstract class EntityA<T extends AssocA> { 

    // Basically, this means myA is at least an AssocA but possibly more... 
    T myA; 
    abstract void meet(); 
} 

abstract class AssocA { 
    int something; 
    abstract void greet(); 
} 

class AssocAConcrete extends AssocA { 
    void greet() { 
     System.out.println("hello"); 
    } 
    void salute() { 
     System.out.println("I am saluting."); 
    } 
} 

class EntityAConcrete extends EntityA<AssocAConcrete> { 
    void meet() { 
     System.out.println("I am about to meet someone"); 
     myA.salute(); 
    } 
} 

Oprócz unikania casting, to również sprawia, że ​​znacznie łatwiej dodać inną funkcjonalność w swoich implementacjach AssocA. Zawsze powinien istnieć sposób na robienie rzeczy bez używania fałszywych implementacji (tj. Metod, które po prostu rzucają "NotImplementedException") lub rzutowania. Chociaż nie zawsze jest to łatwe lub warte czasu na refaktoryzację. Innymi słowy, nikt nie będzie cię winić za rzucanie (cóż ... może niektórzy ludzie będą, ale nie możesz zadowolić wszystkich).

EDIT (Uwagi dotyczące instancji):

Z uwagi @pitosalas' poniżej ...

//Won't work...can't call 'new' on abstract class AssocA 
AssocAConcrete myAssoc = new Assoca(); 

//Instead, do this... 
AssocAConcrete myAssoc = new AssocAConcrete(); 

a potem ....

// Again, won't work. T is only declaring the type inside your class/method. 
// When using it to declare a variable, you have to say EXACTLY what you're making, 
// or at least something as exact as the methods you're trying to invoke 
EnitityA<T extends AssocA> myEntity = new EntityA<AssocAConcrete>(); 

//Instead do this... 
EnitityA<AssocAConcrete> myEntity = new EntityAConcrete(); 

// Or this... 
EntityAConcrete myEntity = new EntityAConcrete(); 

I to powinno być dobrym ...

// Assuming this is defined as `public void setAssoc(T newAssoc) {this.myA = newAssoc;}` 
myEntity.setAssoc(myAssoc); 
myAssoc.salute(); 
+1

Chciałbym móc dać ci +2. Właśnie do tego służą generics; i nie rozumiem, dlaczego niektórzy ludzie unikają tego. Aha, i jestem jedną z osób, które BYłyby odpowiedzialne za stosowanie odlewania w celu rozwiązania problemu, który jest lepiej rozwiązywany za pomocą leków generycznych. –

+0

Ah interesujące. I ja bym je stworzył tak? – pitosalas

+0

Sposób tworzenia instancji jest różny. Gdzie jest kod, którego obecnie używasz do utworzenia wystąpienia? Czy mógłbyś zaktualizować swoje pytanie, aby mieć przykład? – Dave

3

Wygląda podejrzanie. Nie ma nic strasznego w castowaniu, ale w tym przypadku możesz rozwiązać problem, wprowadzając metodę salute do. Podklasy AssocA mogą zapewnić ich implementacje; to część korzyści z dziedziczenia.

Co robisz teraz mówi wszystko EntityA przypadki mają instancję AssocA, ale potem w swojej metodzie meet w zasadzie wymusić wystąpienie AssocA być instancją AssocAConcrete. Ta część jest podejrzana; dlaczego istnieje AssocA, jeśli naprawdę potrzebujesz AssocAConcrete.

Inną opcją (opartą na twoich komentarzach) jest wywołanie salute w metodzie greet. W ten sposób określona podklasa ma określone zachowanie greet zdefiniowane w nadklasie i robi to, co chce. W takim przypadku salute może stać się prywatny lub chroniony. Inna implementacja może z łatwością zrobić coś innego, np. runLikeHell.

+0

Ten rodzaj logicznego sprzężenia między rodzinami klas jest bardzo powszechny. Niestety, system typów nie obsługuje wyrażania go. – ewernli

+0

@nwgotcodes - niezupełnie to samo. Chcę ukryć wiedzę na temat realizacji dwóch konkretnych klas "A", aby każdy z nich wiedział o sobie nawzajem, ale tylko Asocetoncrete wie, jak się przywitać. AssocXConcrete może nie wiedzieć, jak to zrobić. – pitosalas

+0

następnie wywołaj 'salute' w swojej metodzie powitania. – hvgotcodes

0

Problem równoległych hierarchii klas jest bardzo realny i naprawdę do kitu. Logiczne sprzężenie, które AssocAConcrete zawsze jest zgodne z EntityAConcrete, nie może być wyrażone za pomocą systemu typu.

Nie można wyspecyfikować typu myA w EntityAConcrete jako AssocAConcrete, bez ukrywania go przed nadklasą. Myślę, że najbliżej to zajęło się "Family polymorphism", ale to nie jest główny nurt.

0

Jeśli masz dużą część kodu, w którym używasz określenia „Mya” może zadeklarować inną odniesienie tak:

public AssocAConcrete myAConcrete = (AssocAConcrete)myA; 

teraz można korzystać z nowej odniesienie myAConcrete i dostęp do funkcji AssocAConcrete Klasa.

Jeśli chcesz zrobić to podobnie do hvgotcodes, powinieneś rozważyć przeniesienie metody do klasy AssocA.

Powiązane problemy