简单的理解多态
多态,简而言之就是同一个行为具有多个不同表现形式或形态的能力。比如说,有一杯水,我不知道它是温的、冰的还是烫的,但是我一摸我就知道了。我摸水杯这个动作,对于不同温度的水,就会得到不同的结果。这就是多态。
那么,java中是怎么体现多态呢?我们来直接看代码:
public class Water { public void showTem(){ System.out.println("我的温度是: 0度"); } } public class IceWater extends Water { public void showTem(){ System.out.println("我的温度是: 0度"); } } public class WarmWater extends Water { public void showTem(){ System.out.println("我的温度是: 40度"); } } public class HotWater extends Water { public void showTem(){ System.out.println("我的温度是: 100度"); } } public class TestWater{ public static void main(String[] args) { Water w = new WarmWater(); w.showTem(); w = new IceWater(); w.showTem(); w = new HotWater(); w.showTem(); } } //结果: //我的温度是: 40度 //我的温度是: 0度 //我的温度是: 100度
这里的方法showTem()就相当于你去摸水杯。我们定义的water类型的引用变量w就相当于水杯,你在水杯里放了什么温度的水,那么我摸出来的感觉就是什么。就像代码中的那样,放置不同温度的水,得到的温度也就不同,但水杯是同一个。
想必你也看出来了,这段代码中最关键的就是这一句
Water w = new WarmWater();
多态的条件
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象。
继承也可以替换为实现接口。
向上转型与向下转型
向上转型
子类引用的对象转换为父类类型称为向上转型。通俗地说就是是将子类对象转为父类对象。此处父类对象可以是接口。
案例演示
看一个大家都知道的例子:
public class Animal { public void eat(){ System.out.println("animal eatting..."); } } public class Cat extends Animal{ public void eat(){ System.out.println("我吃鱼"); } } public class Dog extends Animal{ public void eat(){ System.out.println("我吃骨头"); } public void run(){ System.out.println("我会跑"); } } public class Main { public static void main(String[] args) { Animal animal = new Cat(); //向上转型 animal.eat(); animal = new Dog(); animal.eat(); } } //结果: //我吃鱼 //我吃骨头
这就是向上转型,Animal animal = new Cat();
将子类对象Cat转化为父类对象Animal。这个时候animal这个引用调用的方法是子类方法。
转型过程中需要注意的问题
向上转型时,子类单独定义的方法会丢失。比如上面Dog类中定义的run方法,当animal引用指向Dog类实例时是访问不到run方法的,
animal.run()
会报错。子类引用不能指向父类对象。
Cat c = (Cat)new Animal()
这样是不行的。
向上转型的好处
减少重复代码,使代码变得简洁。
提高系统扩展性。
举个例子:比如我现在有很多种类的动物,要喂它们吃东西。如果不用向上转型,那我需要这样写:
public void eat(Cat c){ c.eat(); } public void eat(Dog d){ d.eat(); } //...... eat(new Cat()); eat(new Cat()); eat(new Dog());
一种动物写一个方法,如果我有一万种动物,我就要写一万个方法,写完大概猴年马月都过了好几个了吧。好吧,你很厉害,你耐着性子写完了,以为可以放松一会了,突然又来了一种新的动物,你是不是又要单独为它写一个eat方法?开心了么?
那如果我使用向上转型呢?我只需要这样写
public void eat(Animal a){ a.eat(); } eat(new Cat()); eat(new Cat()); eat(new Dog());
恩,搞定了。代码是不是简洁了许多?而且这个时候,如果我又有一种新的动物加进来,我只需要实现它自己的类,让他继承Animal就可以了,而不需要为它单独写一个eat方法。是不是提高了扩展性?
向下转型
与向上转型相对应的就是向下转型了。向下转型是把父类对象转为子类对象。
案例演示
先看一个例子:
//还是上面的animal和cat dog Animal a = new Cat(); Cat c = ((Cat) a); c.eat(); //输出 我吃鱼 Dog d = ((Dog) a); d.eat(); // 报错 : java.lang.ClassCastException:com.chengfan.animal.Cat cannot be cast to com.chengfan.animal.Dog Animal a1 = new Animal(); Cat c1 = ((Cat) a1); c1.eat(); // 报错 : java.lang.ClassCastException:com.chengfan.animal.Animal cannot be cast to com.chengfan.animal.Cat
为什么第一段代码不报错呢?想必你也知道了,因为a本身就是Cat对象,所以它理所当然的可以向下转型为Cat,也理所当然的不能转为Dog,你见过一条狗突然就变成一只猫这种操蛋现象?
而a1为Animal对象,它也不能被向下转型为任何子类对象。比如你去考古,发现了一个新生物,知道它是一种动物,但是你不能直接说,啊,它是猫,或者说它是狗。
向下转型注意事项
向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
向下转型只能转型为本类对象(猫是不能变成狗的)。
大概你会说,我特么有病啊,我先向上转型再向下转型??
我们回到上面的问题:喂动物吃饭,吃了饭做点什么呢?不同的动物肯定做不同的事,怎么做呢?
public void eat(Animal a){ if(a instanceof Dog){ Dog d = (Dog)a; d.eat(); d.run();//狗有一个跑的方法 } if(a instanceof Cat){ Cat c = (Cat)a; c.eat(); System.out.println("我也想跑,但是不会"); //猫会抱怨 } a.eat();//其他动物只会吃 } eat(new Cat()); eat(new Cat()); eat(new Dog()); //.....
现在,你懂了么?这就是向下转型的简单应用,可能举得例子不恰当,但是也可