关于Java子接口子类型

时间:2012-05-04 16:10:17

标签: java interface

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

我有一个界面Pipeline,它有一个方法execute(Stage)

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

BookStageStage延伸。

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

有关于此的任何建议吗?

3 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分:4)

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

假设您的接口Pipeline包含方法execute(Stage),扩展接口BookPipeline包含execute(BookStage)

还假设您有一些实现Conc的课程BookPipeline

考虑以下

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

会发生什么?这将是不安全的!
Java希望避免它,从而首先防止这种情况发生。

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

答案 2 :(得分:1)

为了详细说明@amit的答案,代码段不安全,因为Conc.execute方法将BookStage作为参数,这将尝试挤压Stage那个地方(当然,并非所有Stage都是BookStage s)。

但是,想象一下我们想要采用另一种方式,即将BookePipeline.execute a super 类型的Stage设为参数类型,例如{{1 }}

所以只是为了澄清,我们会:

Object

interface Pipeline { void execute(Stage s); } interface BookPipeline extends Pipeline { @Override void execute(Object s); } 实施Conc的地方:

BookPipeline

理论上,这是安全的,因为没有违反Liskov Substitutability - 我们可以安全地将Pipeline p = new Conc(); p.execute(new Stage()); 传递给任何带有Stage参数或更大参数的实现。这被称为逆变。 Java不支持逆变参数类型,但有languages这样做。

您的原始问题涉及协变参数类型,由于指定的原因而不安全(但奇怪的是,一种名为Eiffel的语言允许这样做。)

但是,Java确实支持协变返回类型。想象一下Stage有一个

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)。

因此,为了重新规定与规则重写相关的规则,一个覆盖方法的扩展类/接口,只能使这些方法更接近接受它们并且更多具体返回的内容。(虽然在Java的情况下,只允许更具体的返回类型。)

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