2012-05-04 10 views
5

To wydaje się podstawowe pytanie java.Podpis podtypu Java o podtypach

Mam jeden interfejs, Pipeline, który ma metodę execute(Stage).

Następnie tworzenia interfejsu sub rozciąga się od Pipeline, np BookPipeline, to jak metoda być execute(BookStage).

BookStage rozciąga się od Stage.

Wygląda na to, że tego rodzaju definicja nie mogła przejść kompilacji java.

Jakieś sugestie na ten temat?

+1

Co znajduje się komunikat o błędzie kompilatora? Jaki jest twój kod? –

Odpowiedz

6

Możesz rozważyć użycie generycznych.

public interface Pipeline<T extends Stage> { 
    public void execute(T stage); 
} 

public interface BookPipeline extends Pipeline<BookStage> { 
    @Override 
    public void execute(BookStage stage); 
} 
+4

Czy deklaracja 'execute' w' BookPipline' nie jest czerwona? –

+0

@KirkWoll Tak, jest tam ze względu na przykład – Jeffrey

+0

Mam cię, nie muszę go przesłonić. Dzięki wielkie! – BlueDolphin

4

Oprócz tego, co @Jeffrey napisał jako możliwe rozwiązanie, ważne jest, aby zrozumieć, dlaczego nie można zrobić.

Załóżmy, że miał interfejs Pipeline z metodą execute(Stage) i rozszerzania interfejsu BookPipeline z execute(BookStage).

Załóż również, że masz klasę Conc, która implementuje BookPipeline.

Rozważmy następujący

Pipeline p = new Conc(); 
p.execute(new Stage()); 

Co się stało? To będzie niebezpieczne!
Java chce tego uniknąć, a tym samym zapobiegać tym sytuacjom od samego początku.

Reguła jest rozszerzenie klasy/interfejsu może dodać zachowanie, ale nie zmniejszyć go.

+0

Bardzo dobry punkt, dzięki! – BlueDolphin

0

Wystarczy rozwinąć odpowiedź @amit „s, fragment kodu jest niebezpieczny jako metoda Conc.execute zajmuje BookStage jako parametr i to byłoby próbuje wycisnąć Stage w miejscu, które (i oczywiście nie wszystkie Stage s to BookStage s).

jednak wyobrazić chcieliśmy iść w drugą stronę, to znaczy uczynić typ parametru z BookePipeline.executeSuper rodzaj Stage, takich jak Object.

Więc po prostu do wyjaśnienia, to mamy:

interface Pipeline 
{ 
    void execute(Stage s); 
} 

interface BookPipeline extends Pipeline 
{ 
    @Override 
    void execute(Object s); 
} 

i gdzie Conc narzędzia BookPipeline:

Pipeline p = new Conc(); 
p.execute(new Stage()); 

To teoretycznie być bezpieczne, ponieważ Liskov Substytucyjność nie została naruszona - my można bezpiecznie przekazać Stage do dowolnej implementacji, która ma parametr Stage lub wyższy. Jest to znane jako contrawariancja. Java nie obsługuje typów argumentów contravariant, jednak są to: languages.

Twoje oryginalne pytanie dotyczy typów argumentów, które nie są bezpieczne z podanych powodów (jednakże, o dziwo, zezwala na to język nazwany Eiffel).

Java obsługuje typy covariant , zwracając typy. Wyobraź sobie Pipeline miał

Stage getAStage(); 

byłoby idealnie prawną BookPipeline, aby zastąpić tę metodę tak:

@Override 
BookStage getAStage(); 

Następnie wyobrazić mieliśmy:

public void someMethodSomewhere(Pipeline p) 
{ 
    Stage s = p.getAStage(); 
    //do some dance on Stage 
} 

Zakładając mieliśmy jakąś klasę Donc który zaimplementował Pipeline i przeskoczył getAStage() dokładnie tak, jak jest zdefiniowany w Pipeline (tak wciąż powraca Stage), oba z tych połączeń są OK:

someMethodSomewhere(new Conc()); 
someMethodSomewhere(new Donc()); 

Ponieważ zawsze możemy umieścić Stage lub coś mniej (np BookStage) w zmiennej typu Stage.

Tak, aby przeredagować reguły odnoszą się w szczególności do metody nadrzędnych, rozciągający Klasa/interfejs, który zastępuje metod, mogą jedynie te metody bardziej ogólnych w co oni zaakceptować i bardziej szczegółowych w co wrócą. (choć w przypadku Javy, są dozwolone tylko bardziej specyficzne typy powrotne).

Wystarczy pamiętać, Pécs - Producent rozciąga Consumer super (Joshua Bloch, Efektywna Java)