Java的转型之前一直处于似是而非的状态。
其中尤其是向上转型,大概意思谁不懂啊,A a = new B() 嘛,然而往细了抠就有点蒙蔽,尤其是面对考卷里出现的那种贼JB恶心的多态题,简直要哭了。
翻阅了几篇大佬们写的博客,整理一下思路总结如下。
首先先从最基本的解起:
向上转型即A a = new B() 也就是父类的引用指向了子类的对象
A a; //创建父类A对象的引用a
a = new B() //new了一个子类对象B被父类的引用a所指向
这也就是向上转型,此时a指向了一个子类的对象
由于父类引用a指向了子类的对象B,因此可以有B b = (B)a,因为a已经指向了子类所以可以强转成B
但如果引用a指向的是正常的父类对象A,也就是 A a = new A();那么对于B b = (B) a;会运行出错;
这是因为对于第一个例子而言子类的引用当然可以指向子类的对象,而对于第二个例子而言子类的对象妄图去指向父类的对象,越俎代庖?
有了最基本的转型的概念,下面举一个向上转型的小例子:
public class Animal {
public void eat(){
System.out.println("animal eatting...");
}
}
class Bird extends Animal{
public void eat(){
System.out.println("bird eatting...");
}
public void fly(){
System.out.println("bird flying...");
}
}
class Main{
public static void main(String[] args) {
Animal b=new Bird(); //向上转型
b.eat();
//! error: b.fly(); b虽指向子类对象,但此时丢失fly()方法
}
}
结果是bird eatting...
而fly方法是子类特有的方法,对应变量b而言由于是父类的引用并不认识子类特有的方法,所以b.fly会报错
简而言之 b实际指向的是子类,因此会调用子类本身的方法。同时向上转型时引用变量b会遗失除与父类对象共有的其他方法,因此fly()不为b所识别。
上面举个向上转型的小例子帮助了解,那么在实际项目中有什么用处呢?
个人感觉提高了代码的复用性,举个小例子;
public class Human {
public void sleep() {
System.out.println("Human sleep..");
}
}
class Male extends Human {
@Override
public void sleep() {
System.out.println("Male sleep..");
}
}
class Female extends Human {
@Override
public void sleep() {
System.out.println("Female sleep..");
}
}
class Main{
public static void main(String[] args) {
dosleep(new Male()); //传入的参数是子类
dosleep(new Female());
}
public static void dosleep(Human h) { //方法里的参数是父类
h.sleep();
}
}
结果:Male sleep…
Female sleep…
为什么说传入的参数是子类的对象而方法里的参数是父类引用呢,是因为只有这样才是向上转型所对应的
Human h = new Male(); Human h = new Female();
可以看到,由于使用的向上转型,那么Main类里只需要一个dosleep方法即可,程序会根据传入的参数自动去调用对应的子类方法。
一般在大型项目开发中,往往是多个类实现了同一个接口或继承同一个父类,如果说不使用向上转型,那么也就是子类每有一个父类方法在父类中都需要书写一个方法
这样会造成整个项目的冗杂,而采用转型的概念后,父类或者接口中只需要有一个方法即可,这样会使上层逻辑抽象,独立清晰方便以后扩展。
结尾在分析一下博客里出现很多的恶心例子来巩固一下
public class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
public class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
public class C extends B{
}
public class D extends B{
}
public class Test {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
结果
1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D
这个恶心的例子应该不止一次看到,我也来掺一脚给一下我自己的解法
对于前三个而言均是a1的方法调用,变量a1是直接指向对象A的,即最常见的A a = new A()的情况,那么可能的答案就只有A and A 和 A and D两种
对于前两个参数分别是b和c,在A的两个方法里没有直接的指向,因此想到C继承B继承A,所以输出结果既是A and A
对于第三个参数是d,在A中有明确的方法,因此输出A and D (这个应该是最不会出错的)
对于4~6而言均是a2的方法调用,变量a2是父类的引用指向子类对象B,参考前文在转型的过程中会遗失方法show(B obj),又由于方法show(A obj)继承自父类A
因此可能的答案只有B and A 和 A and D两种
所以对于第四个和第五个的参数b和c,由于方法show(B obj)遗失了,因此只能找到最近的show(A obj),又由于这个方法被子类所重写,因此输出的是子类方法中的B and A而非父类的A and A
对于第六个参数是d ,在父类中有该方法,因此输出A and D
对于7~9而言均是b的方法调用,变量b指向的是子类B的对象,而子类B继承了父类A,因此可能的答案为B and B ,B and A ,A and D,父类的A and A被重写了。
因此7~9的输出也不再有什么问题了。
参考博客:http://blog.youkuaiyun.com/thinkGhoster/article/details/2307001
http://blog.youkuaiyun.com/mr_jj_lian/article/details/6860845