这是多态性背后的基本原理。每个对象的类型是出现在表达式左侧的内容,但是,它的行为与右侧的内容一样。例如,让我们考虑一个这样的函数:
public static void Claw(Cat cat)
{
cat.claw();
}
现在,我们假设我们的类是这样的:
public class Cat
{
//...
public void claw()
{
System.out.println("Cat claw");
}
}
public class JungleCat extends Cat
{
//...
@Override
public void claw()
{
System.out.println("Jungle cat claw");
}
}
现在,让我们实例化一些对象:
Cat c = new Cat();
Cat j = new JungleCat();
传入我们的static Claw
函数:
Claw(c); //Prints "Cat claw"
Claw(j); //Prints "Jungle cat claw"
假设我们像这样修改了函数:
public static void Claw(JungleCat junglecat)
{
junglecat.claw();
}
然后Claw(c)
显然不会编译,因为它是一个Cat
. 怎么样Claw(j)
?这也不会编译,因为我们已经说过它也是 type Cat
。我们知道它实际上是一个JungleCat
,但我们已经告诉编译器“好吧,把它当作一个普通Cat
类型来对待”。
在许多这些初学者示例中经常缺少的是为什么有人想要这样做的动机。猫和动物等通常是不好的例子。举个更好的例子,假设我们有一些代码根据运行的操作系统来做不同的事情;说移动文件。Linux
如果它/usr/bin
在C:\Windows\System32
. 所以我们定义一个这样的接口:
public interface FileMover
{
public void move(File f);
}
public class LinuxMover implements FileMover
{
public void move(File f)
{
//Move our file to /usr/bin
}
}
public class WindowsMover implements FileMover
{
public void move(File f)
{
//Move our file to C:\Windows\System32
}
}
现在,直到运行时我们才知道这段代码将在哪个系统上运行——所以我们可以进行运行时检查并在运行时实例化正确的类:
//Pseudocode
FileMover fm;
if(OperatingSystem == LINUX) {
fm = new LinuxMover();
}
else if(OperatingSystem == WINDOWS) {
fm = new WindowsMover();
}
fm.move();
同样,任何函数都可以简单地采用 a FileMover
- 他们不在乎是什么类型- 因此我们不必在每次想要使用不同类型时编写两个函数定义FileMover
,例如doSomething(WindowsFileMover m)
and doSomething(LinuxFileMover m)
,我们可以简单地将其替换为一个函数doSomething(FileMover m)
,传入正确的类型,它将“正常工作”。
这可能比问题所需的信息多得多,但希望它也能让您对为什么这些事情以比通常的猫和狗更具体的方式完成有一些直觉。