为什么不允许以下接口合同?

时间:2012-06-29 10:48:22

标签: java interface

我正在考虑为Java提供一个新功能,我想问一下到目前为止它为何受到设计的限制:

public abstract class BodyPart {
    abstract public void followBodyPart(BodyPart part);
}

public class Head extends BodyPart{
     public void followBodyPart(Body body ) { //Why is this kind of implementation not allowed? 
     ...
     }
}
public class Body extends BodyPart{
     public void followBodyPart(Head head ) { //and this
     ...
     }
     public void followBodyPart(Forearm leftForearm ) { //and also this
     ...
     }
     ...
}
//Arm, Forearm, etc...

为什么Head中的followBodyPart(Body body)没有在BodyPart中实现followBody?如果愿意,优势就会很明显。

首先,IDE可以在其内部提供自动完成功能Body对象作为followBody的参数,而不是Head无法遵循的任何其他BodyParts对象。

其次,Body的当前版本由一个函数和许多instanceof组成,可以消除。

最后,泛型可以在这里提供帮助,但不能解决问题,因为此代码应该移植到Java ME设备。

我已经在不合适的论坛上提出了这个问题,因为我发现here

关于答案,我邀请你思考不同。我知道任何实施BodyPart的东西都应该接受任何 BodyPart,但是:我想要的是能够说Head可以接受 A BodyPart。

谢谢。

2 个答案:

答案 0 :(得分:3)

您在链接的论坛帖子中也回答了这个问题..

即;接口定义函数应该能够接受实现BodyPart任何

通过在Head中实现的函数接受子类Body,而不是任何其他子类;您违反了该合同(因为它不再接受任何实施BodyPart的内容)。

接口通常用于提供“外部”代码,允许他们确保提供接口的任何实现;他们肯定可以使用界面定义的功能。

因此,如果此外部代码获得BodyPart,则它知道它具有函数followBodyPart,可以接受任何扩展BodyPart作为参数。但是,该外部代码永远不会知道它得到Head(或者可以在检查实例后转换它),因此无法知道接口函数接受{{ 1}}。


按要求;假设你提供Body接口作为某种程序API。在这种情况下,我不需要知道它是什么类型的BodyPart。现在说我有两个;通过API中的某些功能收到,例如签名:BodyPart。该方法声明可能public BodyPart getBody()我回来了;但它也可能是别的东西(事实是,我不知道!)。

根据Body界面;我可以在第一个BodyPart上调用followBodyPart,然后将第二个调用作为参数。但是,实际BodyPart实施不允许这样做;我无法知道这一点。

如果你真的希望不同的班级接受不同的参赛作品;你应该从Body中删除函数,然后在子类中实现它。

通过API传递这些子类;每个人都知道他们正在谈论什么,以及它能做些什么(例如BodyPartpublic Body getBody())。因为我有实际的实现类,它们具有某个public Head getHead()到“跟随”的实际实现,所以它不是问题。

另一个选择是 - 但在你的问题中说不可能 - 使用泛型;在这种情况下,您可以定义一个接口说明:

BodyPart

例如,API可以传回实现的public interface Accepts<T extends BodyPart> { public void followBodyPart(T part); } BodyPart实例。 (编辑:正如我在这里写的那样,我忘了记住你不能用不同的泛型类型实现相同的接口;因此通用接口方法需要实际的实现来封装实际可以处理调用的对象,一切都更糟糕)

奖金编辑:当然,您也可以将Accepts<Head>AcceptsHead作为界面并有效解决泛型问题:)。

我希望这个编辑能够清除为什么拥有一个通用接口(使用AcceptsArm作为参数)是一个奇怪的(也是坏的)想法,但只指定(可能隐藏的)实现类中的特定实现。

答案 1 :(得分:0)

首先,我并不是很直观地理解你的阶级关系 - 它们是循环的,已经表明了糟糕的设计。我并不是说你不需要那个特定的结构 - 我只是建议一些重构去除圆形可能最终是一个更好的设计。

您尝试做的是实现访问者模式。但是如果你有对基类的引用,它就永远不会触发专门方法的调用 - 例如因为编译器不能选择你想要的方法,所以运行时只需要为你做切换实例 - 它最多只是语法糖(查找scala,它们实际上就是这样)。 / p>

def bodyPart(part:BodyPart) =>
  part match {
     Head(h) => /* do something with head h */
     Foot(f) => /* do something with foot f */
     Toe(t) => /* do something with toe t */
  }

另一种解决方法是抽象地删除所有可能的访问者类型:

public class BodyPart { // could have been abstract class
    public void followBodyPart(BodyPart part) { }
    public void followBodyPart(Head part) { }
    public void followBodyPart(Arm part) { }
    public void followBodyPart(Foot part) { }
    public void followBodyPart(Toe part) { }
}
public class Head { ... /* only implements Head, BodyPart, others error */ }
public class Arm { ... /* only implements Arm, Abdomen, etc */ }

现在访问者调用者将在编译时静态选择正确的方法。但是在每个实现中需要更多的管道,因为它需要决定如何正确处理所有其他输入类型。但这是一件好事 - 它消除了歧义。