这似乎是一个基本的java问题。
我有一个接口,Pipeline
它有一个方法execute(Stage)
。
然后我创建一个子接口来扩展Pipeline
,比如说BookPipeline
,我喜欢这个方法execute(BookStage)
。
BookStage
从 延伸Stage
。
似乎这种定义无法通过 java compile。
对此有什么建议吗?
您可能需要考虑使用泛型。
public interface Pipeline<T extends Stage> {
public void execute(T stage);
}
public interface BookPipeline extends Pipeline<BookStage> {
@Override
public void execute(BookStage stage);
}
除了@Jeffrey 写的可能的解决方案之外,了解您为什么不能这样做也很重要。
假设您有一个Pipeline
带有方法的接口execute(Stage)
,以及一个BookPipeline
带有execute(BookStage)
.
还假设您有一些Conc
实现BookPipeline
.
考虑以下
Pipeline p = new Conc();
p.execute(new Stage());
会发生什么?会很不安全!
Java 想要避免它,从而从一开始就防止这种情况。
规则是扩展类/接口可以添加行为,但不能减少行为。
只是为了详细说明@amit 的答案,代码片段是不安全的,因为该Conc.execute
方法将 aBookStage
作为参数,这将试图挤压 aStage
来代替它(当然,并非所有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());
从理论上讲,这将是安全的,因为没有违反 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()
Pipeline
Stage
someMethodSomewhere(new Conc());
someMethodSomewhere(new Donc());
因为我们总是可以将 aStage
或任何更少(例如BookStage
)放入类型为 的变量中Stage
。
因此,将规则改写为专门与方法覆盖相关,覆盖方法的扩展类/接口只能使这些方法在它们接受的内容上更通用,在返回的内容上更具体。(尽管在 Java 的情况下,只允许更具体的返回类型。)
请记住,PECS - Producer Extends,Consumer Super(Joshua Bloch,Effective Java)