举个例子:有两个类,Father是父类,Son类继承自Father。
Father f1 =new Son() ; //这就叫upcasting(向上转型 )
//现在f1 引用一个指向 一个Son 对象
Son s1 = (Son)f1 ; //这个叫downcasting(向下转型 )
//现在f1还是指向Son对象
第二个例子:
Father f2 =new Father();
Son s2=(Son)f2; //出错,子类引用不能直接指向父类对象
你或许会问,在第一个例子中:Son s1=(Son)f1;为什么是正确的呢?
很简单因为f1指向一个子类对象,Father f1=new Son();子类s1引用当然可以指向子类对象了。
而f2被传给了一个Father对象,Father f2=new Father();子类s1引用不能指向父类对象。
总结:
1.父类引用指向子类对象,子类引用不能指向父类对象。
2.把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转型。
如Father f1=new Son();
3.把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转型。
如f1就是一个指向子类对象的父类引用,把f1赋给子类引用s1 即Son s1=(Son)f1;
其中f1前面的(Son)必须添加,进行强制转换。
4.upcasting 会丢失子类特有的方法(下面有说明),但是子类overriding( 重写)父类的方法,子类方法有效
5.向上转型的作用, 减少重复代码(如下所述)
我们在Java编程中经常碰到类型转换,对象类型转换主要包括向上转型和向下转型。
package a.b;
public class A {
public void a1() {
System.out.println("Superclass");
}
}
A的子类B:
package a.b;
public class B extends A {
public void a1() {
System.out.println("Childrenclass"); //覆盖父类方法
}
public void b1(){} //B类定义了自己的新方法
}
C类:
package a.b;
public class C {
public static void main(String[] args) {
A a = new B(); //向上转型
a.a1();
}
}
如果运行C,输出的是Superclass还是Childrenclass?不是你原来预期的Superclass,而是Childrenclass。这是因为a实际上指向的是一个子类对象。当然,你不用担心,Java虚拟机会自动准确地识别出究竟该调用哪个具体的方法。不过,由于向上转型,a对象会遗失和父类不同的方法,例如b1()。有人可能会提出疑问:这不是多此一举吗?我们完全可以这样写:
B a = new B();
a.a1();
确实如此!但这样就丧失了面向抽象的编程特色,降低了可扩展性。其实,不仅仅如此,向上转型还可以减轻编程工作量。来看下面的显示器类Monitor:
package a.b;
public class Monitor{
public void displayText() {}
public void displayGraphics() {}
}
液晶显示器类LCDMonitor是Monitor的子类:
package a.b;
public class LCDMonitor extends Monitor {
public void displayText() {
System.out.println("LCD display text");
}
public void displayGraphics() {
System.out.println("LCD display graphics");
}
}
阴极射线管显示器类CRTMonitor自然也是Monitor的子类:
package a.b;
public class CRTMonitor extends Monitor {
public void displayText() {
System.out.println("CRT display text");
}
public void displayGraphics() {
System.out.println("CRT display graphics");
}
}
等离子显示器PlasmaMonitor也是Monitor的子类:
package a.b;
public class PlasmaMonitor extends Monitor {
public void displayText() {
System.out.println("Plasma display text");
}
public void displayGraphics() {
System.out.println("Plasma display graphics");
}
}
现在有一个MyMonitor类。假设没有向上转型,MyMonitor类代码如下:
package a.b;
public class MyMonitor {
public static void main(String[] args) {
run(new LCDMonitor());
run(new CRTMonitor());
run(new PlasmaMonitor());
}
public static void run(LCDMonitor monitor) {
monitor.displayText();
monitor.displayGraphics();
}
public static void run(CRTMonitor monitor) {
monitor.displayText();
monitor.displayGraphics();
}
public static void run(PlasmaMonitor monitor) {
monitor.displayText();
monitor.displayGraphics();
}
}
可能你已经意识到上述代码有很多重复代码,而且也不易维护。有了向上转型,代码可以更为简洁:
package a.b;
public class MyMonitor {
public static void main(String[] args) {
run(newLCDMonitor()); //向上转型
run(newCRTMonitor()); //向上转型
run(newPlasmaMonitor()); //向上转型
}
public static void run(Monitor monitor) { //父类实例作为参数
monitor.displayText();
monitor.displayGraphics();
}
}
我们也可以采用接口的方式,例如:
package a.b;
public interface Monitor {
abstract void displayText();
abstract void displayGraphics();
}
将液晶显示器类LCDMonitor稍作修改:
package a.b;
public class LCDMonitor implements Monitor {
public void displayText() {
System.out.println("LCD display text");
}
public void displayGraphics() {
System.out.println("LCD display graphics");
}
}
CRTMonitor、PlasmaMonitor类的修改方法与LCDMonitor类似,而MyMonitor可以不不作任何修改。
可以看出,向上转型体现了类的多态性,增强了程序的简洁性。
2、
A类:
package a.b;
public class A {
void aMthod() {
System.out.println("A method");
}
}
A的子类B:
package a.b;
public class B extends A {
void bMethod1() {
System.out.println("B method 1");
}
void bMethod2() {
System.out.println("B method 2");
}
}
C类:
package a.b;
public class C {
public static void main(String[] args) {
A a1 = new B(); // 向上转型
a1.aMthod(); // 调用父类aMthod(),a1遗失B类方法bMethod1()、bMethod2()
B b1 = (B) a1; // 向下转型,编译无错误,运行时无错误
b1.aMthod(); // 调用父类A方法
b1.bMethod1(); // 调用B类方法
b1.bMethod2(); // 调用B类方法
A a2 = new A();
B b2 = (B) a2; // 向下转型,编译无错误,运行时将出错
b2.aMthod();
b2.bMethod1();
b2.bMethod2();
}
}
从上面的代码我们可以得出这样一个结论:向下转型需要使用强制转换。运行C程序,控制台将输出:
Exception in thread "main"java.lang.ClassCastException:
A method
A method
B method 1
B method 2
其实黑体部分的向下转型代码后的注释已经提示你将发生运行时错误。为什么前一句向下转型代码可以,而后一句代码却出错?这是因为a1指向一个子类B的对象,所以子类B的实例对象b1当然也可以指向a1。而a2是一个父类对象,子类对象b2不能指向父类对象a2。那么如何避免在执行向下转型时发生运行时ClassCastException异常?使用instanceof就可以了。我们修改一下C类的代码:
A a2 = new A();
if (a2 instanceof B) {
B b2 = (B) a2;
b2.aMthod();
b2.bMethod1();
b2.bMethod2();
}
这样处理后,就不用担心类型转换时发生ClassCastException异常了。
总结:
1、向上转型时父类对象可以访问子类中从父类继承或重写的方法,但无法访问子类新增加的方法。
2、向下转型即把父类对象转换为子类对象。
3、将父类对象转换为子类对象时,先要判断父类对象是否是子类的实例,如果不是却执行该语句,会抛出ClassCastException异常。即类转型异常。
4、向下转型需要强制转换。向下转型之前需要先进行向上转型。