2010-12-11 8 views
7

Więc zadałem to pytanie wcześniej, ale popełniłem błąd w kodzie, który podniósł większość ludzi, a nie sam problem.Java: podstawianie podklasy/podtypu parametru podczas nadpisywania metody?

W każdym razie staram się przesłonić metodę interfejsu w klasie. Jednak chcę, aby typ parametru w metodzie przesłaniania był podklasą typu parametru zdefiniowanego w metodzie overriden.

Interfejs jest:

public interface Observer { 
public void update(ComponentUpdateEvent updateEvent) throws Exception; 
} 

Podczas gdy klasy, który zastępuje ten sposób jest następujący:

public class ConsoleDrawer extends Drawer { 

//... 

@Override 
public void update(ConsoleUpdateEvent updateEvent) throws Exception { 
    if (this.componentType != updateEvent.getComponentType()) { 
    throw new Exception("ComponentType Mismatch."); 
    } 
    else { 
    messages = updateEvent.getComponentState(); 
    } 
} 

//... 

} 

ConsoleUpdateEvent to podklasa ComponentUpdateEvent.

Teraz mógłbym mieć metodę update() w ConsoleDrawer jako parametr ComponentUpdateEvent, a następnie przesłać ją do ConsoleUpdateEvent, ale jeśli to możliwe, szukam nieco bardziej eleganckiego rozwiązania. Anyhelp byłby doceniony. Dziękuję Ci.

+5

Nie sądzę, że jest to możliwe, ponieważ jest sprzeczne z zasadą interfejsu (tj. Klasy, które implementują interfejs, będą dokładnie pasować do wszystkich sygnatur metody określonych przez interfejs). Jednak będę obserwować to pytanie, ponieważ jestem bardzo zainteresowany, aby zobaczyć, czy ktoś inny mnie nie wykaże. – DGH

Odpowiedz

2

Możesz wypróbować następujące rzeczy. Funkcja @Deprecated generuje ostrzeżenie, jeśli kompilator wie, że wywołasz pierwszą metodę zamiast drugiej.

@Override @Deprecated 
public void update(ComponentUpdateEvent updateEvent) { 
    // throws a ClassCastException if its not the right type. 
    update((ConsoleUpdateEvent) updateEvent); 
} 

public void update(ConsoleUpdateEvent updateEvent) { 
    messages = updateEvent.getComponentState(); 
} 

BTW: Należy nie tylko umieścić rzuca wyjątek na wszystko. To na pewno nie najlepsza praktyka.

EDYCJA: Mam zaimplementowane inne rozwiązanie tego problemu, który działa dobrze z OSGi, ale może pracować w dowolnym miejscu.

An Observer rejestruje się za pomocą brokera i spodziewa się znaleźć metody z adnotacją taką jak ObserverCallback.

np.

public class ConsoleDrawer extends Drawer { 
@ObserverCallback 
public void onConsoleUpdateEvent(ConsoleUpdateEvent updateEvent) { 
    messages = updateEvent.getComponentState(); 
} 
} 

public class DeviceDrawer extends Drawer { 
@ObserverCallback 
public void onDeviceUpdateEvent(DeviceUpdateEvent updateEvent) { 
    // do something. 
} 
} 

W pierwszym przypadku broker znajduje metodę z funkcją @ObserverCallback, która przyjmuje jeden argument. Jest to jedyny typ, który Broker przekaże. Druga klasa oczekuje innego typu. Obserwatorzy mogą mieć wiele metod/typów, dzięki czemu mogą obsługiwać różne wiadomości w różnych metodach odpowiednich dla danego typu. Wiesz również, że nigdy nie otrzymasz danych, których się nie spodziewasz.

+0

Ja osobiście (i wiele innych osób, z którymi rozmawiałem) nie lubię używania znacznika Przestarzałego do niczego innego niż pierwotny cel: metody oznaczania, które były prawidłowe, ale teraz są nieaktualne. Używanie go do czegoś innego sprawia wrażenie hackera. – DGH

+0

@DGH, Metoda była ważna (dla rodzica), ale nie jest już ważna dla tej klasy. ;) Rozumiem jednak twój punkt widzenia. Idealnie byłoby tag, który powodował błąd kompilatora. Jeszcze lepiej byłoby zaprojektować metodę update(), tak aby cała implementacja honorowała umowę. –

+0

Dzięki za sugestię. Korzystanie z pierwszego rozwiązania; Zmieniłem nazwę drugiej metody aktualizacji na prywatną pustkę addEventMessages (ConsoleUpdateEvent) {// ...} i wydaje się, że działa ona dość dobrze. – carnun

7

Nie możesz. To nie jest Eiffel. Problem polega na tym, że możesz użyć interfejsu, aby wywołać metodę implementacji z niekompatybilnym typem. Zatem parametry kowariancyjne nie są dozwolone. Przeciwwstrząsowe parametry również nie są dozwolone, ale łatwiej jest zapewnić przeciążenie. Typ kowariantności jest dozwolony (od 1.5).

Można sparametryzuj interfejs:

public interface Observer<T extends ComponentEvent> { 
    void update(T event) throws Exception; 
} 

Ewentualnie użyć bardziej sensowne interfejs:

public interface ConsoleObserver { 
    void update(ConsoleEvent event) throws Exception; 
} 
+1

Znaleziono szczegółową odpowiedź tutaj http://stackoverflow.com/a/9421315/1276636 – Sufian

2

jadąc zasad OOP, sub-klasa powinna być użyteczny w dokładnie taki sam sposób, jak klasa nadrzędna. np.

Observer ob = new ConsoleDrawer(); 
ob.update(new ComponentUpdateEvent()); // This needs to work always. 

Jednakże, jeśli Java miały pozwalają na skorzystanie z podtypu parametru gdy przesłanianie metody, wówczas byłoby to narazić swój kod do przypadków, w których metoda nadrzędnym (w podklasie) odrzuci parametr wejściowy (ComponentUpdateEvent w powyższym przypadku). W ten sposób nigdy nie będziesz pewny, czy można go bezpiecznie wywołać(), czy też nie na referencji Observer.

W związku z tym jedynym logicznym rozwiązaniem jest zaakceptowanie parametru klasy nadrzędnej, należy go sprawdzić, a następnie przesłać do wymaganego podtypu.

Powiązane problemy