2012-05-04 2 views
5

Это кажется основным вопросом Java.О подтипе подтекста Java

У меня есть один интерфейс, Pipeline, который имеет способ execute(Stage).

Затем я создаю дополнительный интерфейс для расширения от Pipeline, скажем BookPipeline. Мне нравится метод execute(BookStage).

BookStage распространяется от Stage.

Похоже, что такое определение не может передать java-компиляцию.

Любое предложение об этом?

+1

Что сообщение об ошибке компилятора? Какой у вас код? –

ответ

6

Возможно, вы захотите рассмотреть использование дженериков.

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

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

Не является ли объявление 'execute' в' BookPipline' redudant? –

+0

@KirkWoll Да, он есть ради примера – Jeffrey

+0

Gotcha, мне не нужно его переопределять. Спасибо! – BlueDolphin

4

В дополнение к тому, что написал @Jeffrey в качестве возможного решения, важно понять, почему вы не можете это сделать.

Предположим, вы имели интерфейс Pipeline с методом execute(Stage) и расширяющейся интерфейс BookPipeline с execute(BookStage).

Также предположим, что у вас есть класс Conc, который реализует BookPipeline.

Рассмотрим следующий

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

Что будет? Это будет небезопасно!
Java хочет избежать этого и, таким образом, предотвратить эти ситуации с первого места.

Правило Расширяемый класс/интерфейс может добавить поведение, но не уменьшить его.

+0

Очень хороший момент, спасибо! – BlueDolphin

0

Просто уточнить ответ @amit «s, то фрагмент кода является небезопасным как метод Conc.execute принимает BookStage в качестве параметра, и это будет пытаться выжать Stage вместо этого (и, конечно, не все Stage s - BookStage s).

Однако, представьте, что мы хотели пойти в другую сторону, то есть, сделать тип параметра из BookePipeline.execute типа А супер из Stage, таких как Object.

Так просто уточнить, мы имеем:

interface Pipeline 
{ 
    void execute(Stage s); 
} 

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

А где Conc орудия BookPipeline:

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

Это, в теории, быть безопасным, потому что Лисков подставляемость не была нарушена - мы может безопасно передать Stage в любую реализацию, которая приняла параметр Stage или больше. Это известно как контравариантность. Java не поддерживает contravariant типы аргументов, однако есть languages, которые делают.

Ваш первоначальный вопрос относится к ковариантны типов аргументов, который является небезопасным по причинам, указанным (однако ни странно, язык называется Eiffel позволяет).

Java все же поддерживает ковариант . Представьте Pipeline был

Stage getAStage(); 

было бы вполне законно BookPipeline переопределить этот метод, как так:

@Override 
BookStage getAStage(); 

Тогда представьте, что мы имели:

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

Предположим, что мы имели некоторый класс Donc который реализовал Pipeline и перегрузил getAStage() точно так, как он определен в Pipeline (так еще возвращаясь Stage), оба из этих вызовов в порядке:

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

Потому что мы всегда можем поставить Stage или что-нибудь меньше (например, BookStage) в переменной типа Stage.

Так перефразировать правило относиться конкретно к переопределению метода, выступающему класс/интерфейсу, который переопределяет методы, может сделать только те методы более общих в том, что они принимают и более конкретных в том, что они возвращаются. (хотя в случае Java, только более конкретные типы возвращаемых разрешены.)

Просто помните, Печ - Производитель Расширяет, Consumer Супер (Джошуа Блох, Эффективное Java)

Смежные вопросы