Java:多态性是否只适用于具有相同签名的方法?

Java:多态性是否只适用于具有相同签名的方法?,java,parameters,polymorphism,factory,Java,Parameters,Polymorphism,Factory,我所见过的多态方法重写的唯一例子涉及不带参数的方法,或者至少具有相同的参数列表的方法。考虑常见的动物/狗/猫的例子: public abstract class Animal { public abstract void makeSound(); } public class Dog extends Animal { public void makeSound() { System.out.println("woof"); } } public

我所见过的多态方法重写的唯一例子涉及不带参数的方法,或者至少具有相同的参数列表的方法。考虑常见的动物/狗/猫的例子:

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?
    }
}
在main方法的最后一行(注释),我不知道该放什么,因为我不知道我有什么类型的动物。我以前不必担心这个问题,因为makeSound()不接受任何参数。但是speak()可以,参数取决于动物的类型


我读过一些语言,比如Objective-C,允许变量参数列表,所以这样的问题永远不会出现。有人知道用Java实现这类功能的好方法吗?

据我所知,Java不允许这样做。说话(名字、生命)现在只是猫的功能。有些语言确实允许这种灵活性。要强制java允许这样做,可以将paramater设置为对象数组或其他集合


但是,考虑到当你调用Salk时,你现在必须知道要传递哪些参数,所以这一点有点莫名其妙。

a.speak("Gerorge");
您不需要知道实例化了什么类型的动物,因为这是多态性的目标。此外,由于您已使用以下语句:

super.speak(name);
猫和狗都有动物的行为加上自己的行为。

你可以这样做

public void speak(Map ... mappedData)
    {
        System.out.println("My name is " + mappedData.get("name")+ " and I have "+mappedData.get("lives");
    }

但是,我建议将lives设置为Cat的实例变量,并让您的工厂传递一个默认值(或者让构造函数为其设置一个默认参数)。

如果您想这样调用,可以使用反射来获取类:

if (a.getclass() == Cat.class) {
// speak like a cat
} else if (a.getclass() == Dog.class) {
.
.
.

当然,这可能不是最好的设计,应该谨慎使用反射。

您的示例
Cat
不再是多态的,因为您必须知道传递该参数是
Cat
。即使Java允许,您将如何使用它?

您混淆了方法重写和方法重载。在您的示例中,
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
上发出警告,因为关键字表示可能的多态抽象失败。

Java也有可变参数列表,但我认为这不是最好的方法,至少不是在所有情况下

当子类具有接口未定义的行为时,Java中没有太多不冗长或有点不稳定的选项

您可以有一个speak(),它接受标记接口并将arg构造委托给工厂。您可以传递一个参数映射。你可以用varargs


最后,您需要知道传递给该方法的内容,不管是哪种语言。

如果您必须知道对象的类型,才能调用speak方法,那么我同意您关于如何真正破坏多态性的评论。如果您绝对必须能够访问这两种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");
    }
}

或者,您可以将UnsupportedOperationException放入子类(或者您可能希望使用选中的异常)我实际上并不提倡这两种方法,但我认为这是实现您要求的最接近的方法,而且我实际上看到过使用类似方法的系统。

我不明白为什么会否决这一点。这是真的,包括免责声明,这可能不是一个好主意\@选民:有什么建议吗?我已经说过这可能不是一个好的解决方案,我只是想指出这个方法。再看一遍,
instanceof
可能是一个更好的方法(因为它也会捕获
Cat
的子类)。@BrendanLong:当然!我又忘了:(可能是因为我正在读Java反射的作用?糟糕的借口,糟糕的借口:)+1抵消不公平的,无法解释的-1。这是一个有效的解决方案(尽管不是非常优雅)。+1这是唯一一个涉及到第二个示例中
Cat
类中speak方法是重载的,而不是重写的事实的答案。+1简单、准确,最重要的是准确的解释。我不想提供答案,因为我不同意这一点,但是Java有varargs。因此,该方法可以定义为
speak(String…args)
speak(String[]args)
。这两种选择都有点弱。
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");
    }
}