这是多态性背后的基本原理。每个对象的类型是出现在表达式的左侧,但是,它的行为就像右侧的任何东西。例如,让我们考虑一个功能,像这样:
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");
}
}
现在,让我们instanstiate一些对象:
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)
呢?这也不会编译,因为我们已经说过它也是Cat
类型。我们知道它实际上是一个JungleCat
,但我们已经告诉编译器“好吧,只要把它看作是一个普通的Cat
类型”。
在很多这些初学者的例子中经常缺少的东西是动机,因为为什么有人会想这样做。猫和动物等往往是不好的例子。举一个更好的例子,假设我们有一些代码是根据它运行的操作系统来做不同的事情的;说移动文件。如果它运行在Linux
上,我们想要将文件移动到/usr/bin
,比方说,如果它在Windows上,那么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();
同样,任何功能可以简单地采取FileMover
- 他们不在乎什么样 - 因此,我们不必每次都写两个函数的定义,我们想用一种不同的FileMover
,如doSomething(WindowsFileMover m)
和doSomething(LinuxFileMover m)
,我们可以简单地repl使用一个功能doSomething(FileMover m)
,传入正确的类型,它会“正常工作”。
这可能比问题要求更多的信息,但希望它也给你一些直觉,为什么这样的事情以比通常的猫和狗稍微更具体的方式完成。
*对象*的类型不一定与它被分配给的变量的类型(或者其他用作表达式)的类型相同。考虑这一点:'Object str =“hello”;' - 分配给'str'的 object *的类型是什么? *表达式* str的类型是什么? – 2012-12-14 05:25:05