1

我正在考虑为 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 设备上。

这个问题已经被问过了,在我在这里发现的不合适的论坛中

关于答案,我邀请您思考不同的问题。我知道任何实现 BodyPart 的东西都应该接受任何BodyPart,但是:我想要的是能够说 Head 能够接受A BodyPart 跟随。

谢谢。

4

2 回答 2

3

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

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

通过实现函数Head只接受子类,而不接受任何其他子类;您违反了该合同(因为它不再接受任何实施)。BodyBodyPart

接口通常用于提供给“外部”代码,让他们可以确定提供了接口的哪个实现;他们肯定可以使用接口定义的功能。

因此,如果这个外部代码得到一个BodyPart,它就知道它有一个followBodyPart可以接受任何扩展BodyPart作为参数的函数。但是,该外部代码永远不会知道它得到了Head(或者可以,在实例检查之后强制转换它之后),因此无法知道接口函数将接受一个Body.


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

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

如果你真的希望不同的班级接受不同的条目;您应该从中删除该函数BodyPart并在子类中实现它。

通过从 API 传回这些子类;每个人都知道他们在谈论什么,以及它可以做什么(例如public Body getBody()public Head getHead())。因为我有了实际的实现类,它们具有一定BodyPart“遵循”的实际实现,所以这不是问题。

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

public interface Accepts<T extends BodyPart> {
    public void followBodyPart(T part);
}

例如,API 可以传回已实现BodyPart的 或Accepts<Head>实例。(编辑:当我在这里写这篇文章时,我忘了记住你不能用不同的泛型类型多次实现相同的接口;所以泛型接口方法需要实际的实现来封装可以实际处理调用的对象,使得一切都变得一团糟)

奖金编辑:当然,您也可以将AcceptsHead,AcceptsArm作为接口并有效地解决泛型问题:)。

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

于 2012-06-29T10:55:26.137 回答
0

首先,我不是很直观地理解你的类关系——它们是循环的,这已经表明设计不好。我并不是说您不需要那种特定的结构——我只是建议进行一些重构以消除循环性最终可能是一个更好的设计。

看起来您正在尝试做的是实现访问者模式。但是,如果您有对基类的引用,它永远不会触发专用方法的调用 - 例如,由于编译器无法选择您想要的方法,那么运行时只需要进行实例切换对你来说 - 它充其量只是语法糖(查找scala,他们实际上是这样做的)。

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 */
  }

解决这个问题的另一种方法是抽象地noop所有可能的访问者类型:

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 */ }

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

于 2012-06-29T11:09:25.503 回答