给出一个抽象类
public abstract class AbstractStomach {
public abstract void consume(Food food);
}
我想要一些具有不同重载方法的具体类:
public class FruitStomach extends AbstractStomach {
public static class Apple extends Food {}
public static class Orange extends Food {}
@Override
public void consume(Apple food) {...}
@Override
public void consume(Orange food) {...}
}
public class MeatStomach extends AbstractStomach {
public static class Beef extends Food {}
@Override
public void consume(Beef food) {...}
}
上面的工作并没有成功,因为它迫使我在每个具体的类中实现通用的consume(Food)
抽象方法。
但是我不希望具体的类实现consume(Food)
,因为它们已经由它们的重载变体来处理,实际上,每个具体的类应该只使用不同的特定食物,而不是任何食物,所以
AbstractStomach stomach = new FruitStomach();
stomach.consume(new Beef());
应该给出错误。
如果我在抽象类中删除抽象方法,那么就没有必要让它们扩展抽象类,因为它没有为它们提供实现的指导。
如何修复上述设计,以便每个具体类都可以使用重载方法,但他们知道有一个抽象方法consume()
可以实现,但不需要实现consume(Food)
。
修改
我可以在不使用重载的情况下解决上述问题,例如
public class FruitStomach extends AbstractStomach {
public static class Apple extends Food {}
public static class Orange extends Food {}
@Override
public void consume(Food food) {
if (food instanceof Apple) {
...
} else if (food instanceof Orange) {
...
} else {
// throws an error
}
}
}
但这不是我想要的。我想看一个使用方法重载的解决方案。
答案 0 :(得分:4)
抽象类中的抽象方法不仅仅是提供指导实现的方法"。抽象类允许您拥有可以是抽象类的任何子类的对象。也就是说,您可以使用
之类的方法public void evaluateDigestion(AbstractStomach stomach) {
}
您可以使用任何类型的Stomach
对象,FruitStomach
或MeatStomach
或DessertStomach
或其他任何内容来调用它。在evaluateDigestion
内,该方法可以调用stomach.consume(someKindOfFood)
。由于这是为抽象类定义的consume
(在编译时evaluateDigestion
不知道类是什么),因此consume
使用Food
参数。然后,在运行时,对consume
的呼叫将"发送"到为具体类定义的consume(Food food)
方法。这就是为什么必须为每个具体类定义具有consume
参数的Food
的原因。调度调用不会使用Apple
,Orange
等参数搜索重载方法; Java不会这样做。 (所以你声明它已经被重载变种"处理了,这是不正确的;重载和覆盖之间存在很大的差别,这可能会让新手感到困惑。)
所以你必须写
public void consume(Food food)
在每个具体课程中。实现这一目标的非常笨重的方式是
public void consume(Food food) {
if (food instanceof Apple) {
consume((Apple)food); // calls the overloaded version because you've use a cast to tell Java how to view the object
} else if (food instanceof Orange) {
consume((Orange)food);
} else {
throw ... // some exception that occurs when you try to eat the wrong kind of food
}
}
最好在Food
中定义一个抽象方法,假设Food
是抽象的:
public abstract class Food {
public void abstract consumedBy(AbstractStomach stomach);
}
在Apple
和Orange
等类中定义具体版本,然后编写consume
之类的
public static void consume(Food food) {
food.consumedBy(this);
}
但是,这不会阻止尝试让FruitStomach
消费牛肉。您可能必须使用instanceof
来确保食物兼容。 (一种可能性:定义扩展Fruit
的类Food
,并创建Apple
的{{1}}和Orange
子类;然后您可以说Fruit
要确认它是正确的食物。可能有比这更好的设计模式。我不能想到一个随便的。)
答案 1 :(得分:3)
您可以使用泛型来(部分)解决您的问题:
AbstractStomach定义了一个类型参数T
,它声明了它消耗的Food
类型:
public abstract class AbstractStomach<T extends Food> {
public abstract void consume(T food);
}
然后,您可以声明食物类型Fruit
和相应的FruitStomach
:
public class Fruit extends Food {}
public class FruitStomach extends AbstractStomach<Fruit> {
public static class Apple extends Fruit {}
public static class Orange extends Fruit {}
@Override
public void consume(Fruit food) {}
}
以及类似的课程Meat
和MeatStomach
:
public class Meat extends Food {}
public class MeatStomach extends AbstractStomach<Meat> {
public static class Beef extends Meat {}
@Override
public void consume(Meat food) {}
}
答案 2 :(得分:1)
要@Override方法,您需要具有相同的返回条件或子类型,名称和参数,以便您无法更改&#34;食品食品&#34;在&#34;橘子橙&#34;。
我认为你可以做的是对你需要在具体类中的方法内工作的对象(或者你使用instanceof)进行转换。或者更好的是你可以创建一个对象:
Food orange = new Orange();
Food apple = new Apple();
因此使用Orange类中的第一次方法覆盖,第二次使用Apple类中的一个方法。
如果它不是您想要的解决方案,请发表评论,以便我可以尝试别的。
答案 3 :(得分:1)
The above doesn't work as it is forcing me to implement also the generic consume(Food)
这是正确行为。通过抽象类AbstractStomach
中的合同,您同意,
任何扩展
AbstractStomach
的内容都有一个方法consume(Food)
(任何食物),然后在子类中 强迫它只有两种类型的食物,从而绕过合同。
答案 4 :(得分:1)
您遇到的问题是您的抽象方法签名需要将名为 Food 的超类传递给consume-method。
这个抽象类的每个实现(以及它的抽象方法)都必须覆盖相同的签名。e.g。
public FruitStomach extends AbstractStomach {
public static class Apple extends Food {}
public static class Orange extends Food {}
@Override
public void consume(Food food) {
if (food instanceof Orange) {
Orange orange = (Orange) food;
// do smth. with orange
} else{
// so smth with food
}
}
public MeatStomach extends AbstractStomach {
public static class Beef extends Food {}
@Override
public void consume(Food food) {
Beef beef = (Beef) food;
// do smth. with beef...
}
}
可行。虽然这不是非常干净的代码,但如果您的代码正确连接在一起,则无法进行编译时验证。 但正如你所说,如果你在错误的输入食物上调用错误的Stomach.consume() - 方法,你可能会引发一个ClassCastException,你可以处理它。