我看到的多态方法覆盖的唯一例子涉及不带参数的方法,或者至少具有相同的参数列表。考虑常见的动物/狗/猫示例:
public abstract class Animal
{
public abstract void makeSound();
}
public class Dog extends Animal
{
public void makeSound()
{
System.out.println("woof");
}
}
public class Cat extends Animal
{
public void makeSound()
{
System.out.println("meow");
}
}
public class ListenToAnimals
{
public static void main(String[] args)
{
AnimalFactory factory = new AnimalFactory();
Animal a = factory.getRandomAnimal(); // generate a dog or cat at random
a.makeSound();
}
}
在这种情况下,一切都很好。现在让我们添加另一个在抽象类中部分实现的方法,同时在子类中获得更具体的行为:
public abstract class Animal
{
public abstract void makeSound();
public void speak(String name)
{
System.out.println("My name is " + name);
}
}
public class Dog extends Animal
{
public void makeSound()
{
System.out.println("woof");
}
public void speak(String name)
{
super.speak(name);
System.out.println("I'm a dog");
}
}
public class Cat extends Animal
{
public void makeSound()
{
System.out.println("meow");
}
public void speak(String name, int lives)
{
super.speak(name);
System.out.println("I'm a cat and I have " + lives + " lives");
}
}
public class ListenToAnimals
{
public static void main(String[] args)
{
AnimalFactory factory = new AnimalFactory();
Animal a = factory.getRandomAnimal(); // generate a dog or cat at random
a.makeSound();
// a.speak(NOW WHAT?
}
}
在主要方法的最后一行(注释)中,我不知道该放什么,因为我不知道我有什么类型的动物。之前我没有必要担心,因为makeSound()没有接受任何参数。但是speak()确实如此,并且参数取决于Animal的类型。
我读过一些语言,例如Objective-C,允许变量参数列表,所以不应该出现这样的问题。有人知道在Java中实现这种事情的好方法吗?
答案 0 :(得分:6)
您正在混淆方法覆盖和方法重载。在您的示例中,Cat
类有两种方法:
public void speak(String name) // It gets this from its super class
public void speak(String name, int lives)
重载是一种定义具有类似功能但参数不同的方法的方法。如果你这样命名方法就没有区别了:
public void speakWithLives(String name, int lives)
为避免混淆,java中的建议是在尝试覆盖方法时使用@Override
注释。因此:
// Compiles
@Override
public void speak(String name)
// Doesn't compile - no overriding occurs!
@Override
public void speak(String name, int lives)
编辑:其他答案提到这一点,但我重复一遍以强调。添加新方法使得Cat
类在所有情况下都不再能够表示为Animal
,从而消除了多态性的优势。要使用新方法,您需要将其向下转换为Cat
类型:
Animal mightBeACat = ...
if(mightBeACat instanceof Cat) {
Cat definitelyACat = (Cat) mightBeACat;
definitelyACat.speak("Whiskers", 9);
} else {
// Definitely not a cat!
mightBeACat.speak("Fred");
}
我的IDE中的代码检查工具会在instanceof
上发出警告,因为关键字表示可能出现多态抽象失败。
答案 1 :(得分:5)
您的示例Cat
不再具有多态性,因为您必须知道传递该参数的Cat
。即使Java允许,你会如何使用它?
答案 2 :(得分:2)
据我所知,java不允许你这样做。说(名字,生命)现在只是猫的功能。有些语言确实允许这种灵活性。要强制java允许此操作,您可以将参数设置为对象数组或其他集合。
但是,考虑到当你打电话说话时,你现在必须知道要传递哪些参数,所以这一点有点没有实际意义。
答案 3 :(得分:1)
当您将多态方法称为:
时a.speak("Gerorge");
您不需要知道实例化了哪种类型的动物,因为这是多态性的目标。此外,因为您有用户的句子:
super.speak(name);
Cat and Dog都会拥有Animal的行为加上自己的行为。
答案 4 :(得分:0)
你可以做到
public void speak(Map ... mappedData)
{
System.out.println("My name is " + mappedData.get("name")+ " and I have "+mappedData.get("lives");
}
但是,我建议将生命变成Cat的实例变量并让你的工厂传递一个默认值(或让构造函数有一个默认参数)。
答案 5 :(得分:0)
In this case best way is to use a DTO,
public class SpeakDTO
{
//use getters and setters when you actually implement this
String name;
int lives;
}
public class Dog extends Animal
{
public void speak(SpeakDTO dto)
{
super.speak(dto.name);
System.out.println("I'm a dog");
}
}
public class Cat extends Animal
{
public void speak(SpeakDTO dto)
{
super.speak(dto.name);
System.out.println("I'm a cat and I have " + dto.lives + " lives");
}
}
public class ListenToAnimals
{
public static void main(String[] args)
{
AnimalFactory factory = new AnimalFactory();
Animal a = factory.getRandomAnimal(); // generate a dog or cat at random
a.makeSound();
SpeakDTO dto = new SpeakDTO();
dto.name = "big cat";
dto.lives = 7;
a.speak(dto);
}
}
答案 6 :(得分:0)
如果你想这样打电话,你可以使用反射来获得课程:
if (a.getclass() == Cat.class) {
// speak like a cat
} else if (a.getclass() == Dog.class) {
.
.
.
当然,这可能不是最好的设计,应谨慎使用反射。
答案 7 :(得分:0)
Java也有可变参数列表,但我认为这不是“最佳”方式,至少在所有情况下都不是。
当子类具有未由接口定义的行为时,您在Java中没有很多不冗长或有点不稳定的选项。
你可以有一个speak(),它接受一个标记接口并将arg构造委托给工厂。您可以传递参数图。你可以使用varargs。
最终,无论使用何种语言,您都需要知道要传递给方法的内容。
答案 8 :(得分:0)
如果在调用speak方法之前必须知道对象的类型,我同意有关如何真正打破多态的评论。如果你绝对必须可以访问这两种说话方法,这里有一种方法可以实现它。
public class Animal {
public void speak(String name) {
throw new UnsupportedOperationException("Speak without lives not implemented");
}
public void speak(String name, int lives) {
throw new UnsupportedOperationException("Speak with lives not implemented");
}
}
public class Dog extends Animal {
public void speak(String name) {
System.out.println("My name is " + name);
System.out.println("I'm a dog");
}
}
public class Cat extends Animal {
public void speak(String name, int lives) {
System.out.println("My name is " + name);
System.out.println("I'm a cat and I have " + lives + " lives");
}
}
或者,您可以将UnsupportedOperationExceptions放入子类中(或者您可能希望使用已检查的异常)。 我实际上并没有提倡其中任何一种,但我认为这是实现您所要求的最接近的方式,而且我实际上已经看到使用类似这样的系统。