Java:多态性仅适用于具有相同签名的方法吗?
我见过的多态方法重写的唯一示例涉及不带参数的方法,或者至少具有相同的参数列表。考虑常见的动物/狗/猫的例子:
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中实现这种事情的好方法吗?
The only examples of polymorphic method overriding I ever see involve methods that take no parameters, or at least have identical parameter lists. Consider the common Animal/Dog/Cat example:
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();
}
}
In this case, everything works out just fine. Now let's add another method that gets partially implemented in the abstract class while getting the more specific behavior in the subclasses:
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?
}
}
In that last (commented) line of the main method, I don't know what to put there because I don't know what type of Animal I have. I didn't have to worry about this before because makeSound() didn't take any arguments. But speak() does, and the arguments depend on the type of Animal.
I've read that some languages, such as Objective-C, allow for variable argument lists, so an issue like this should never arise. Is anyone aware of a good way to implement this kind of thing in Java?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
您混淆了方法重写和方法重载。在您的示例中,
Cat
类有两个方法:重载是一种定义具有相似功能但不同参数的方法的方法。如果您这样命名该方法,则没有什么区别:
为了避免混淆,java 中的建议是在尝试重写方法时使用
@Override
注释。因此:编辑:其他答案提到了这一点,但我重复它是为了强调。添加新方法使得
Cat
类在所有情况下都不再能够表示为Animal
,从而消除了多态性的优势。要使用新方法,您需要将其向下转换为Cat
类型:我的 IDE 中的代码检查工具会在
instanceof
上发出警告,因为关键字表明可能多态抽象失败。You are confusing method overriding and method overloading. In your example the
Cat
class has two methods:Overloading is a way to define methods with similar functions but different parameters. There would be no difference if you had named the method thusly:
To avoid confusion the recommendation in java is to use the
@Override
annotation when you are attempting to override a method. Therefore:EDIT: Other answers mention this but I am repeating it for emphasis. Adding the new method made the
Cat
class no longer able to be represented as anAnimal
in all cases, thus removing the advantage of polymorphism. To make use of the new method you would need to downcast it to theCat
type:The code inspection tool in my IDE puts a warning on the
instanceof
as the keyword indicates possible polymorphic abstraction failure.您的示例
Cat
不再是多态的,因为您必须知道它是Cat
才能传递该参数。即使Java允许,你会如何使用它?Your example
Cat
isn't polymorphic anymore, since you have to know it's aCat
to pass that parameter. Even if Java allowed it, how would you use it?据我所知java不允许你这样做。 talk(name,lives) 现在只是 Cat 的功能。有些语言确实允许这种灵活性。要强制 java 允许这样做,您可以将参数设置为对象数组或其他集合。
但是,考虑到当您调用 talk 时,您现在必须知道无论如何要传递哪些参数,因此这一点有点没有实际意义。
As far as I know java doesn't allow you to do that. speak(name, lives) is now just the Cat's function. Some languages do allow this type of flexibility. To force java to allow this, you can make the paramater an array of objects or some other collection.
However, consider that when you call speak, you now must know which parameters to pass in regardless, so the point is somewhat moot.
当您将多态方法调用为:
您不需要知道实例化了哪种类型的 Animal,因为这是多态性的目标。另外,由于您使用了这句话:
猫和狗都会有动物的行为加上自己的行为。
When you call a polymorphic method as:
You don't need to know what type of Animal has instantiated because this is the objective of polymorphism. Also since you have user the sentence:
Both Cat an Dog will have the behavior of Animal plus the own behavior.
你可以做
但是,我建议将生命作为 Cat 的实例变量,并让你的工厂传递默认值(或者让构造函数为其提供默认参数)。
You can do
However, I would advise making lives an instance variable of Cat and have your factory pass a default value (or have the constructor have a default parameter for it).
如果你想进行这样的调用,你可以使用反射来获取类:
当然这可能不是最好的设计,反射应该小心使用。
If you want to make a call like that, you could use reflection to get the class:
Of course this might not be the best design, and reflection should be used with care.
Java 也有可变参数列表,但我认为这不是“最佳”方法,至少不是在所有情况下都是如此。
当子类具有接口未定义的行为时,Java 中没有太多不冗长或有点奇怪的选项。
您可以有一个 talk (),它采用标记接口并将 arg 构造委托给工厂。您可以传递参数映射。您可以使用可变参数。
最终,您需要知道将什么传递给该方法,无论使用哪种语言。
Java also has variable argument lists, but I'd argue that's not the "best" way to do it, at least not in all circumstances.
When subclasses have behavior that isn't defined by the interface, you don't have many options in Java that aren't verbose or a bit wonky.
You could have a speak () that takes a marker interface and delegate arg construction to a factory. You could pass a parameter map. You could use varargs.
Ultimately, you need to know what to pass to the method, no matter what language.
我同意关于如果您必须在调用 talk 方法之前必须知道对象类型的情况下您如何真正破坏多态性的评论。如果您绝对必须能够访问这两种发言方法,那么这里是您可以实现它的一种方法。
或者,您可以将 UnsupportedOperationExceptions 放入子类中(或者您可能希望使用已检查的异常)。 我实际上并不提倡其中任何一个,但我认为这是实现您所要求的最接近的方法,而且我实际上见过使用类似方法的系统。
I agree with the comments about how you've really broken polymorphism if you must know the type of object before you can call the speak method. If you absolutely MUST have access to both speak methods, here is one way you could implement it.
Alternately you can put the UnsupportedOperationExceptions into the child classes (or you might want to used a checked exception). I'm not actually advocating either of these, but I think this is the closest way to implement what you requested, and I have actually seen systems that used something like this.