酒 a = 剑南春
酒 b = 五粮液
酒 c = 酒鬼酒
…
这里所表现的的就是多态。剑南春、五粮液、酒鬼酒都是酒的子类,我们只是通过酒这一个父类就能够引用不同的子类,这就是多态——我们只有在运行的时候才会知道引用变量所指向的具体实例对象。
诚然,要理解多态我们就必须要明白什么是“向上转型”。在继承中我们简单介绍了向上转型,这里就在啰嗦下:在上面的喝酒例子中,酒(Win)是父类,剑南春(JNC)、五粮液(WLY)、酒鬼酒(JGJ)是子类。我们定义如下代码:
JNC a = new JNC();
对于这个代码我们非常容易理解无非就是实例化了一个剑南春的对象嘛!但是这样呢?
Wine a = new JNC();
在这里我们这样理解,这里定义了一个Wine 类型的a,它指向JNC对象实例。由于JNC是继承与Wine,所以JNC可以自动向上转型为Wine,所以a是可以指向JNC实例对象的。这样做存在一个非常大的好处,在继承中我们知道子类是父类的扩展,它可以提供比父类更加强大的功能,如果我们定义了一个指向子类的父类引用类型,那么它除了能够引用父类的共性外,还可以使用子类强大的功能。
更新回答:
其实不是三种情况,是两种情况。
我给你举个实际开发中经常碰到的场景的例子,你就明白为什么“我们经常要把一个对象转型成它的父类或接口来使用了”。
通常我们在使用列表的时候都是这样写的:
而不是
第一种为什么比第二种好?
假设你用了二种方式声明一个列表,然后被其他人引用了(或者被你自己在其他地方用了)。
突然有一天,你发现用ArrayList不好,需要换成LinkedList,那么你肯定要把声明改成:
这样以来,用到你的list的地方都得更改类型声明。更重要的是,用到你的list的人,他们写自己的代码的时候是基于“你的这个list是一个ArrayList ”这样的前提来写的,你突然把它改成了LinkedList,很可能导致他们的程序出错或者需要重写。
但是如果你一开始就用第一种声明方式,就不会有这个问题。你随时可以更换list的,引用你的list的其他人或其他地方不需要任何修改。例如:
所以说,当你声明一个变量的时候,把它的类型声明得越笼统越好,越具体越糟糕。前者对修改是友好的,后者对修改不友好。
类似的,确定一个方法的可访问性(即public、protected、默认的、private)时,在可能的情况下,越小越好。
这个是父类引用子类对象,即是向上转型,是多态的必要条件之一
ArrayList实现了List接口,因此ArrayList肯定是List类型,同理List实现了Collection接口,因此List肯定是Collection类型。也可以理解为ArrayList的向上转型。
父类引用指向子类对象
比如你是一个酒神,对酒情有独钟。某日回家发现桌上有几个杯子里面都装了白酒,从外面看我们是不可能知道这是些什么酒,只有喝了之后才能够猜出来是何种酒。你一喝,这是剑南春、再喝这是五粮液、再喝这是酒鬼酒….在这里我们可以描述成如下: