JavaSE第二十讲:多态详解 续

修改上一节程序:

public class PolyTest2
{
	public static  void main(String[] args)
	{
		Animal animal = new Cat();
		Animal animal2 = new Animal();

		animal2 = animal;
		animal2.sing();
	}
}

class Animal
{
	public void sing()
	{
		System.out.println("animal is singing");
	}
}

class Dog extends Animal
{
	public void sing()
	{
		System.out.println("dog is singing");
	}
}

class Cat extends Animal
{
	public void sing()
	{
		System.out.println("cat is singing");
	}
}
执行结果:

D:\src>java PolyTest2
cat is singing


修改以上第三行代码段程序:

		/*
		Animal animal = new Cat();
		Animal animal2 = new Animal();

		animal2 = animal;
		animal2.sing();
		*/
		Animal animal = new Cat();
		Animal animal2 = new Animal();

		animal = animal2;
		animal.sing();
执行结果:

D:\src>java PolyTest2
animal is singing


继续修改以上代码段:

		/*
		Animal animal = new Cat();
		Animal animal2 = new Animal();

		animal2 = animal;
		animal2.sing();
		*/
		/*
		Animal animal = new Cat();
		Animal animal2 = new Animal();

		animal = animal2;
		animal.sing();
		*/
		Cat cat = new Cat();
		Animal animal = cat;
		animal.sing();
执行结果:

D:\src>java PolyTest2
cat is singing


继续修改此代码段

		/*
		Animal animal = new Cat();
		Animal animal2 = new Animal();

		animal2 = animal;
		animal2.sing();
		*/
		/*
		Animal animal = new Cat();
		Animal animal2 = new Animal();

		animal = animal2;
		animal.sing();
		*/
		/*
		Cat cat = new Cat();
		Animal animal = cat;
		animal.sing();
		*/
		Animal animal = new Animal();
		Cat cat = (Cat)animal;
执行结果:

D:\src>javac PolyTest2.java
D:\src>java PolyTest2
Exception in thread "main" java.lang.ClassCastException: Animal cannot be cast to Cat
        at PolyTest2.main(PolyTest2.java:25)

【说明】:在编译的时候系统只知道Cat是Animal的子类,具体Animal指向谁只有在执行的时候才能够发现,当执行的时候发现animal的引用指向的是animal类型的对象,此时把它强制转换为cat,系统就会提示错误,就好比如将一个人强制的转换成一个男人,这样是不对的。


3. 一共有两种类型的强制转换:
a) 向上类型转换(upcast):比如说将Cat类型转换为Animal类型,即将子转换为父类型。
b) 向下类型转换(downcast):比如将Animal类型转换为Cat类型即将父转换为子类型。对于向下,必须要显式指定(必须要使用强制类型转换)

		//向上类型转换
		Cat cat = new Cat();
		//Animal animal = (Animal)cat;这种写法也可以,但是通常不需要显示的制定,因为向上类型转换通常认为子类就是父类了,不需要强制类型转换。
		Animal animal = cat;
		animal.sing();

		//向下类型转换
		Animal a = new Cat();
		Cat c = (Cat)a;
		c.sing();
【说明】:在向下类型转换中,比如这个程序调用c.sing();方法和调用a.sing();方法执行效果是一样的,为什么还要做这种先下类型转换呢?比如说父类中3个方法,子类继承了或者重写了父类的三个方法,此时使用向下类型转换意义不大,因为调用使用与不使用都是调用子类这三个方法一。但是有一种情况是,假如这个子类自己添加了5个方法,此时父类有3个方法,子类有8个方法,此时如果调用a.sing();这种方法时只能调用在子类当中存在于父类当中的那三个方法,其他五个方法是没法调用到的,因为a是父类型,所以只能调用父类型中有的方法,此时如果使用向下转型之后,调用c.sing();此时c是属于子类型的所以可以调用子类当中的所有方法,这就是使用向下类型转换的原因。

不使用强制类型转换

public class PolyTest3
{
	public static void main(String[] args)
	{		
		//不使用强制类型转换
		Fruit f = new Pear();
		f.grow();

		//使用强制类型转换
	//	Fruit f = new Pear();
	//	Pear p = (Pear)f;
	//	p.grow();
	}
}

class Fruit
{
	public void run()
	{
		System.out.println("fruit is running");
	}
}

class Pear extends Fruit
{
	public void run()
	{
		System.out.println("pear is running");
	}

	public void grow()
	{
		System.out.println("pear is growing");
	}

}
执行结果:

D:\src>javac PolyTest3.java
PolyTest3.java:7: 错误: 找不到符号
                f.grow();
                 ^
  符号:   方法 grow()
  位置: 类型为Fruit的变量 f
1 个错误


使用强制类型转换

public class PolyTest3
{
	public static void main(String[] args)
	{		
		//不使用强制类型转换
	//	Fruit f = new Pear();
	//	f.grow();

		//使用强制类型转换
		Fruit f = new Pear();
		Pear p = (Pear)f;
		p.grow();
	}
}

class Fruit
{
	public void run()
	{
		System.out.println("fruit is running");
	}
}

class Pear extends Fruit
{
	public void run()
	{
		System.out.println("pear is running");
	}

	public void grow()
	{
		System.out.println("pear is growing");
	}

}
执行结果:

D:\src>java PolyTest3
pear is growing


举例一个程序证明多态度是一种运行期的行为,而不是编译期的行为。

public class PolyTest4
{
	public static void main(String[] args)
	{
		A a = null;

		if(args[0].equals("1")) //命令行输入1时执行if里面的语句。
		{
			a = new B();	
		}
		else if(args[0].equals("2"))
		{
			a = new C();
		}
		else if(args[0].equals("3"))
		{
			a = new D();
		}

		a.method();
	}
}

class A
{
	public void method()
	{
		System.out.println("A");
	}
}

class B extends A
{
	public void method()
	{
		System.out.println("B");
	}
}

class C extends A
{
	public void method()
	{
		System.out.println("C");
	}
}

class D extends A
{
	public void method()
	{
		System.out.println("D");
	}
}
执行结果:

D:\src>javac PolyTest4.java
D:\src>java PolyTest4 1
B
D:\src>java PolyTest4 2
C
D:\src>java PolyTest4 3
D

【说明】:编译的时候不知道a指向的是哪个方法,只有在执行的时候输入一个参数来确定具体执行哪个函数。


再举例一个例子来证明多态具体在实际开发中有什么作用。

这个例子需要在PolyTest5类中定义一个run()方法,这个run()方法即处理BMW的方法,又要处理QQ的方法。通常情况都要按下面程序来写。

public class PolyTest5
{
	public void run(BMW bmw)
	{
		bmw.run();
	}

	public void run(QQ qq)
	{
		qq.run();
	}


	public static void main(String[] args)
	{
		PolyTest5 test = new PolyTest5();

		BMW bmw = new BMW();
		test.run(bmw);

		QQ qq = new QQ();
		test.run(qq);
	}
}

class Car
{
	public void run()
	{
		System.out.println("car is running");
	}
}

class BMW extends Car
{
	public void run()
	{
		System.out.println("bmw is running");
	}
}

class QQ extends Car
{
	public void run()
	{
		System.out.println("qq is running");
	}
}
执行结果:

D:\src>java PolyTest5
bmw is running
qq is running

【说明】:这个程序的缺点是如果再添加子类,在子类中又添加方法,此时相应的也需要在PolyTest5类中继续定义run()方法,此种效果比较差,如果用只用一个方法来实现这种机制是最好,所以引申出下面这个程序。


此处利用Java的多态度性将程序修改如下

public class PolyTest5
{
	public void run(Car car)
	{
		car.run();
	}
	public static void main(String[] args)
	{
		PolyTest5 test = new PolyTest5();

		//有以下两种方式。
		//用父类型的引用生成子类型的对象
		Car car = new BMW();
		test.run(car);

		//向上类型转换
		QQ qq = new QQ();
		test.run(qq);

	}
}

class Car
{
	public void run()
	{
		System.out.println("car is running");
	}
}

class BMW extends Car
{
	public void run()
	{
		System.out.println("bmw is running");
	}
}

class QQ extends Car
{
	public void run()
	{
		System.out.println("qq is running");
	}
}
执行结果:

D:\src>java PolyTest5
bmw is running
qq is running

【说明】:这种程序比较容易维护,如果子类继续添加,只要知道父类,子类就都可以作为参数传上去,因为Car是父类型,子类型的引用都可以传上去,实现先上转型,一个方法就可以应付所有的此类需求,这就是多态度给我们带来的好处它屏蔽掉了子类的差异性,用一个公共的父类去标示着接口,不管增加了多少子类,都可以通过将父类型的引用作为一个参数,具体的子类引用传过去就可以了。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值