6

这似乎是一个基本的java问题。

我有一个接口,Pipeline它有一个方法execute(Stage)

然后我创建一个子接口来扩展Pipeline,比如说BookPipeline,我喜欢这个方法execute(BookStage)

BookStage从 延伸Stage

似乎这种定义无法通过 java compile。

对此有什么建议吗?

4

3 回答 3

8

您可能需要考虑使用泛型。

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

public interface BookPipeline extends Pipeline<BookStage> {
     @Override
     public void execute(BookStage stage);
}
于 2012-05-04T16:12:26.593 回答
4

除了@Jeffrey 写的可能的解决方案之外,了解您为什么不能这样做也很重要。

假设您有一个Pipeline带有方法的接口execute(Stage),以及一个BookPipeline带有execute(BookStage).

还假设您有一些Conc实现BookPipeline.

考虑以下

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

会发生什么?会很不安全!
Java 想要避免它,从而从一开始就防止这种情况。

规则是扩展类/接口可以添加行为,但不能减少行为

于 2012-05-04T16:16:05.873 回答
2

只是为了详细说明@amit 的答案,代码片段是不安全的,因为该Conc.execute方法将 aBookStage作为参数,这将试图挤压 aStage来代替它(当然,并非所有Stages 都是BookStages)。

但是,想象一下我们想走另一条路,即将参数类型设为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());

从理论上讲,这将是安全的,因为没有违反 Liskov 可替换性 - 我们可以安全地将 a 传递给Stage任何采用Stage参数或更大参数的实现。这称为逆变。Java 不支持逆变参数类型,但是有些语言支持。

您最初的问题与协变参数类型有关,由于指定的原因,这是不安全的(但奇怪的是,一种名为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()PipelineStage

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

因为我们总是可以将 aStage或任何更少(例如BookStage)放入类型为 的变量中Stage

因此,将规则改写为专门与方法覆盖相关,覆盖方法的扩展类/接口只能使这些方法在它们接受的内容上更通用,在返回的内容上更具体(尽管在 Java 的情况下,只允许更具体的返回类型。)

请记住,PECS - Producer Extends,Consumer Super(Joshua Bloch,Effective Java)

于 2015-01-06T15:38:53.290 回答