java 向上,向下转型

在对Java学习的过程中,对于转型这种操作比较迷茫,特总结出了此文。例子参考了《Java编程思想》。

    目录

几个同义词

向上转型与向下转型

  例一:向上转型,调用指定的父类方法

  例二:向上转型,动态绑定

  例三:向上转型,静态绑定

  例四:向下转型

转型的误区

  1.运行信息(RTTI)

  2.数组类型

  3.Java容器

几个同义词

  首先是几组同义词。它们出现在不同的书籍上,这是造成理解混淆的原因之一。

  父类/超类/基类

  子类/导出类/继承类/派生类

  静态绑定/前期绑定

  动态绑定/后期绑定/运行时绑定

向上转型与向下转型

例一:向上转型,调用指定的父类方法

class Shape {  static void draw(Shape s) {
        System.out.println("Shape draw.");
    }
}

class Circle extends Shape {  static void draw(Circle c) {
        System.out.println("Circle draw.");
    }
}

public class CastTest {
    public static void main(String args[]) {
        Circle c = new Circle();
        Shape.draw(c);
    }
}

输出为

Shape draw.

  这表明,draw(Shape s)方法本来被设计为接受Shape引用,但这里传递的是Circle引用。实际上draw(Shape s)方法可以对所有Shape类的导出类使用,这被称为向上转型。表现的行为,和方法所属的类别一致。换句话说,由于明确指出是父类Shape的方法,那么其行为必然是这个方法对应的行为,没有任何歧义可言。

  “向上转型”的命名来自于类继承图的画法:根置于顶端,然后逐渐向下,以本例中两个类为例,如下图所示:

例二:向上转型,动态绑定

class Shape {
    public void draw() {
        System.out.println("Shape draw.");
    }
}

class Circle extends Shape {
    public void draw() {
        System.out.println("Circle draw.");
    }
}

public class CastTest {
    public static void drawInTest(Shape s) {
        s.draw();
    }
    public static void main(String args[]) {
        Circle c = new Circle();
        drawInTest(c);
    }
}

输出为

  Circle draw.

  这样做的原因是,一个drawInTest(Shape s)就可以处理Shape所有子类,而不必为每个子类提供自己的方法。但这个方法能能调用父类和子类所共有的方法,即使二者行为不一致,也只会表现出对应的子类方法的行为。这是多态所允许的,但容易产生迷惑。

例三:向上转型,静态绑定

class Shape {
    public static void draw() {
        System.out.println("Shape draw.");
    }
}

class Circle extends Shape {
    public static void draw() {
        System.out.println("Circle draw.");
    }
}

public class CastTest {
    public static void drawInTest(Shape s) {
        s.draw();
    }
    public static void main(String args[]) {
        Circle c = new Circle();
        drawInTest(c);
    }
}

输出为

  Shape draw.

  例三与例二有什么区别?细看之下才会发现,例三里调用的方法被static修饰了,得到了完全不同的结果。

  这两例行为差别的原因是:Java中除了static方法和final方法(包括private方法),其他方法都是动态绑定的。对于一个传入的基类引用,后期绑定能够正确的识别其所属的导出类。加了static,自然得不到这个效果了。

  了解了这一点之后,就可以明白为什么要把例一写出来了。例一中的代码明确指出调用父类方法,而例三调用哪个方法是静态绑定的,不是直接指明的,稍微绕了一下。

例四:向下转型

  出自《Java编程思想》8.5.2节,稍作了修改,展示如何通过类型转换获得子类独有方法的访问方式。

  这相当于告诉了编译器额外的信息,编译器将据此作出检查。

class Useful {
    public void f() {System.out.println("f() in Useful");}
    public void g() {System.out.println("g() in Useful");}
}

class MoreUseful extends Useful {
    public void f() {System.out.println("f() in MoreUseful");}
    public void g() {System.out.println("g() in MoreUseful");}
    public void u() {System.out.println("u() in MoreUseful");}

}

public class RTTI {
    public static void main(String[] args) {
        Useful[] x = {
            new Useful(),
            new MoreUseful()
        };
        x[0].f();
        x[1].g();
        // Compile-time: method not found in Useful:
        //! x[1].u();
        ((MoreUseful)x[1]).u(); // Downcast/RTTI
        ((MoreUseful)x[0]).u(); // Exception thrown
    }
}    

输出

Exception in thread "main" java.lang.ClassCastException: Useful cannot be cast to MoreUseful
at RTTI.main(RTTI.java:44)
f() in Useful
g() in MoreUseful
u() in MoreUseful

  虽然父类Useful类型的x[1]接收了一个子类MoreUseful对象的引用,但仍然不能直接调用其子类中的u()方法。如果需要调用,需要做向下转型。这种用法很常见,比如一个通用的方法,处理的入参是一个父类,处理时根据入参的类型信息转化成对应的子类使用不同的逻辑处理。

  此外,父类对象不能向下转换成子类对象

  向下转型的好处,在学习接口时会明显地体会出来(如果把实现接口看作多重继承)。可以参考9.4节的例子,这里不做详述:

interface CanFight {
    void fight();
}
interface CanSwim {
    void swim();
}
interface CanFly {
    void fly();
}

class ActionCharacter {
    public void fight() {}
}

class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly {
    public void swim() {}
    public void fly() {}
}

public class Adventure {
    static void t(CanFight x) { x.fight(); }
    static void u(CanSwim x) { x.swim(); }
    static void v(CanFly x) { x.fly(); }
    static void w(ActionCharacter x) { x.fight(); }
    public static void main(String[] args) {
        Hero i = new Hero();
        t(i); // Treat it as a CanFight
        u(i); // Treat it as a CanSwim
        v(i); // Treat it as a CanFly
        w(i); // Treat it as an ActionCharacter
    }
}

转型的误区

  转型很方便,利用转型可以写出灵活的代码。不过,如果用得随心所欲而忘乎所以的话,难免要跌跟头。下面是几种看似可以转型,实际会导致错误的情形。

1.运行信息(RTTI)

/* 本例代码节选自《Java编程思想》14.2.2节 */

Class<Number> genericNumberClass = int.class

  这段代码是无效的,编译不能通过,即使把int换为Integer也同样不通过。虽然int的包装类Integer是Number的子类,但Integer Class对象并不是Number Class对象的子类。

2.数组类型

/* 代码节改写《Java编程思想》15.8.2节,本例与泛型与否无关。 */

class Generic<T> {}

public class ArrayOfGeneric {
    static final int SIZE = 100;
    static Generic<Integer>[] gia;
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        //! gia = (Generic<Integer>[]) new Object[SIZE];
        gia = (Generic<Integer>[]) new Generic[SIZE];
    }
}

  注释部分在去掉注释后运行会提示java.lang.ClassCastException。这里令人迷惑的地方在于,子类数组类型不是父类数组类型的子类。在异常提示的后面可以看到

[Ljava.lang.Object; cannot be cast to [LGeneric;

  除了通过控制台输出的异常信息,可以使用下面的代码来看看gia究竟是什么类型:

        Object[] obj = new Object[SIZE];
        gia = (Generic<Integer>[]) new Generic[SIZE];
        System.out.println(obj.getClass().getName());
        System.out.println(gia.getClass().getName());
        System.out.println(obj.getClass().getClass().getName());
        System.out.println(gia.getClass().getSuperclass().getName());

控制台输出为:

[Ljava.lang.Object;
[LGeneric;
java.lang.Object
java.lang.Object

  可见,由Generic<Integer>[] gia和Object[] obj定义出的gia和obj根本没有任何继承关系,自然不能类型转换,不管这个数组里是否放的是子类的对象。(子类对象是可以通过向上转型获得的,如果被转换的确实是一个子类对象,见例四)

3.Java容器

/* 代码节选自《Java编程思想》15.10节*/
class Fruit {}
class Apple extends Fruit {}
class Orange extends Fruit {}
public class Test {
    public static void main(String[] args) {
    // 无法编译
    List<Fruit> fruitList = new ArrayList<Apple>();
    }
}

出处:http://www.cnblogs.com/wuyuegb2312/p/3858521.html
时间: 2024-10-12 21:28:17

java 向上,向下转型的相关文章

java(向上向下转型)

在Java编程中经常碰到类型转换,对象类型转换主要包括向上转型和向下转型. 5.13.1 向上转型 我们在现实中常常这样说:这个人会唱歌.在这里,我们并不关心这个人是黑人还是白人,是成人还是小孩,也就是说我们更倾向于使用抽象概念"人".再例如,麻雀是鸟类的一种(鸟类的子类),而鸟类则是动物中的一种(动物的子类).我们现实中也经常这样说:麻雀是鸟.这两种说法实际上就是所谓的向上转型,通俗地说就是子类转型成父类.这也符合Java提倡的面向抽象编程思想.来看下面的代码: package a.

2.25 Java基础总结 ①多态②向上向下转型③instanceof

①多态②向上向下转型③instanceof 一.多态即相同行为,不同实现有两种方法:重载和重写分类:①静态多态:编译时已经确定效果,所用重载实现(不是指static)②动态多态:编译未知,运行已知(使用动态绑定和重写实现) 动态绑定:父类的引用指向子类的对象,执行相应的子类方法,而不是父类的,从而实现多态性 二.向上转型,向下转型向上转型:一个父类的引用可以指向不同的子类对象,或者说一个子类的对象可以被当做一个父类类型低精度向高精度转pet = cat;自动转,子类向父类 向下转型:强制转,有很

从向上向下转型到----抽象类接口(一)

对象的多态性-向上向下转型 向上转型:子类对象变为父类对象,格式:父类 父类对象 = 子类实例,自动转换; 向下转型:父类对象变为子类对象,格式:子类 子类对象 = (子类) 父类实例,强制转换; 注意:对象的多态性和方法复写是联系在一起的 向上转型: class A{ public void print(){ System.out.println("a") } } class B extends A{ public void print(){ System.out.println(&

Java之路 - 抽象类、接口、多态、向上向下转型

1.抽象类 如果父类当中的方法不确定如何进行{}方法实现,那么这就是一个抽象方法 抽象方法所在的类必须是抽象类 使用方法: (1)不能直接创建new抽象类对象 (2)必须用一个子类来继承抽象父类 (3)子类必须覆盖重写抽象父类当中所有的抽象方法 覆盖重写(实现):去掉抽象方法的abstract关键字,然后补上方法体大括号: (4)创建子类对象进行使用 public abstract class animal { //抽象类 public abstract void eat(); //这是一个抽象

Java面向对象之多态(向上向下转型) 入门实例

一.基础概念 多态: 重点是对象的多态性.某一事物的多种体现形态. 多态的作用: 1.提高了代码的扩展性,后期出现的功能,可以被之前的程序所执行. 2.不能使用子类特有的功能.只能使用覆盖父类的功能. 多态的前提: 1.必须要有继承关系或者实现关系. 2.通常对方法进行重写.覆盖. 3.父类或者接口要引用指向子类对象. 多态向上转型: 1.提高程序的扩展性,隐藏了具体子类型 2.只能使用父类中的功能,不能使用子类特有的功能.功能被限定了. 多态向下转型: 1.可以使用子类型的特有功能. 2.必须

对象的向上向下转型规则

对象的转型:向上转型:子类-->父类向下转型:父类-->子类 例如: Class B extends AA a;B b;向上转型是自动的:b = new B();a = b; 向下转型要强制转换:a = new B();b = (B)a; 正确 a = new A();b = (B)a; 错误   *并且在向下转型之前要进行向上转型:即通过a=new B();得到的: *否则会报java.lang.ClassCastException: note.A cannot be cast to not

java-多态的向上向下转型

package com.多态; public class Demo1 { /** * @param args * int i=10; * byte j=20; * i=j;//自动类型提升 * j=(byte)i;//自动类型转换 */ public static void main(String[] args) { Animal2 a=new Cat();//父类引用指向子类对象.就是向上转型 System.out.println(a.num);//10,a引用只能看到父类对象属性值 a.ea

JAVA 多态向下转型

package Demo513.Multi; public abstract class Animal { public static void main(String[] args) { Animal animal =new Cat(); animal.eat(); //往下转型:必须保证对象创建的就是猫, Cat cat=(Cat) animal; //就可以引用子类:Cat类的方法 cat.CatchMice(); Animal Dog=new Dog(); Dog.eat(); //测试

Java 多态 向下转型

向下转型: 子类引用指向父类对象,此处必须强制转换,可以调用子类特有方法.例: Animal two = new Cat(); Cat temp = (Cat)two; 若满足转型条件时才能进行强转. 判断一个对象是否是某个类的实例: 原文地址:https://www.cnblogs.com/CPU-Easy/p/12262276.html

8.JAVA-向上转型、向下转型

父子对象之间的转换分为了向上转型和向下转型,它们区别如下: 向上转型 : 通过子类对象(小范围)实例化父类对象(大范围),这种属于自动转换 向下转型 : 通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换 1. 向上转型 示例1-向上转型 class A { public void print() { System.out.println("A:print"); } } class B extends A { public void print() { System.ou