开始的时候,总是不理解,为什么Java中要有转型这一概念。但是随着面向对象思想的加深,以及对Java的理解深入。才知道,转型真的是Java的特色,更是好东西啊。
话不多说,直接举例。
首先,我们有两个类:父类:动物类,子类:人
public class Animal {
public void eat(){
System.out.println("animal eatting...");
}
}
class Human extends Animal{
public void eat(){
System.out.println("human eatting...");
}
public void study(){
System.out.println("human study...");
}
}
class Main{
public static void main(String[] args) {
Animal b=new Human(); //向上转型
b.eat();
b.study(); //此处提示在Animal中没有定义study方法。
}
在函数中,将对象Human向上转型为父类Animal对象,调用父类的eat方法。这样看起来没有什么,但是,实际上在我们实际编程中节约了很大的代码量。想象一下,如果没有向上转型的情况。(粘贴一下别人的代码,转自六易金鳞http://blog.csdn.net/shanghui815/article/details/6088588)
来看下面的显示器类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(new LCDMonitor()); //向上转型
run(new CRTMonitor()); //向上转型
run(new PlasmaMonitor()); //向上转型
}
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可以不不作任何修改。
可以看出,向上转型体现了类的多态性,增强了程序的简洁性。
回到我们的Animal上面来。为什么,调用study()方法会出错呢?因为向上转型父类指向子类引用对象会遗失除与父类对象共有的其他方法,也就是在转型过程中,子类的新有的方法都会遗失掉,在编译时,系统会提供找不到方法的错误。但是如果子类重写了父类的方法就根据这个引用指向调用子类重写的这个方法(这个方法就是覆盖override)。这个调用过程就称为“动态绑定”。
好了,再来说一下向下转型:
主要分为两种情况:
情况一:如果父类引用的对象如果引用的是指向的子类对象,那么在向下转型的过程中是安全的。也就是编译是不会出错误的。
情况二:如果父类引用的对象是父类本身,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现java.lang.ClassCastException错误。它可以使用instanceof来避免出错此类错误。
还是用Animal的例子:
public class Animal {
public void eat(){
System.out.println("animal eatting...");
}
}
class Human extends Animal{
public void eat(){
System.out.println("human eatting...");
}
public void study(){
System.out.println("human study...");
}
}
class Main{
public static void main(String[] args) {
Animal b=new Human(); //向上转型
b.eat();
Human h = (Human)b;//向下转型,编译和运行皆不会出错
h.eat();
h.study();
Animal b=new Animal();
b.eat();
//Human h2 = (Human)b2;//不安全的向下转型,编译无错但会运行会出错
//h2.eat();
//h2.study();
//以此这种向下转型应该这样写
if(b2 instanceof Human){
Human h2 = (Human)b2;
h2.eat();
h2.study();
}
总结:
1、父类引用可以指向子类对象,子类引用不能指向父类对象。
2、把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转型。就像例子:Animal animal = new Human();
3、把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转型。
如Animal就是一个指向子类对象的父类引用,把Animal赋给子类引用human 即Human human =(Human)animal;其中animal前面的(Human)必须添加,进行强制转换。
4、向上转型(upcasting) 会丢失子类特有的方法,但是子类覆盖(overriding) 父类的方法,子类方法有效
5、向上转型的作用,减少重复代码,父类为参数,调有时用子类作为参数,就是利用了向上转型。这样使代码变得简洁。体现了JAVA的抽象编程思想。
每天进步一点点
推荐微信公众号【排骨肉段】,记录日常的美好。