类型转换与多态

类型检查

Java 的任意变量和引用经过 类型声明(type declaration),才能使用。我们之前见过对象数据、类数据、方法参数、方法返回值以及方法内部的自动变量,它们都需要声明其类型。

Java是一种 强类型(strongly typing)语言,它会对类型进行检查。如果我们错误的使用类型,将造成错误。

比如在下面的 Test 类中,我们将一个 Cup 类对象赋予给 aPerson 类引用:

class Human
{   
    public Human(int h)
    {
        this.height = h;
    }

    public int getHeight()
    {
       return this.height;
    }

    public void growHeight(int h)
    {
        this.height = this.height + h;
    }

    private int height;
}


class Cup 
{
    public void addWater(int w) 
    {
        this.water = this.water + w;
    }

    public void drinkWater(int w)
    {
        this.water = this.water - w;
    }

    private int water = 0;
}


public class Test
{
    public static void main(String[] args)
    {
        Human aPerson;            
        aPerson = new Cup();     
    }
}

这将出现报错信息:

java:43: 错误: 不兼容的类型: Cup无法转换为Human
        aPerson = new Cup();
                  ^
1 个错误




基本类型转换

Java 可以对基本类型的变量进行类型转换。不同的基本类型有不同的长度和存储范围。

如果我们从一个高精度类型转换到低精度类型,比如从 float 转换到 int,那么我们有可能会损失信息。这样的转换叫做 收缩变换(narrowing conversion)。这种情况下,我们需要显示的声明类型转换,比如:

public class Test
{
    public static void main(String[] args)
    {
        int a;
        a = (int) 1.23;  // 收缩变换
        System.out.println(a);
    }
}

输出结果:

1

如果我们从低精度类型转换成高精度类型,则不存在信息损失的顾虑。这样的变换叫做 宽松变换(widening conversion)。我们不需要显示的要求类型转换,Java 可以自动进行:

public class Test
{
    public static void main(String[] args)
    { 
        int a = 3;
        double b;
        b = a;  // 宽松变换
        System.out.println(a);
    }
}

输出结果:

3
基本类型转换




upcast 与多态

在 Java 中,引用也可以进行类型转换,但是有限制。

我们可以将一个衍生类引用转换为其基类引用,这叫做 向上转换(upcast)或者 宽松转换

下面的 BrokenCup 类继承自 Cup 类,并覆盖了 Cup 类中原有的 addWater()drinkWater() 方法:

class Cup 
{
    public void addWater(int w) 
    {
        this.water = this.water + w;
    }

    public void drinkWater(int w)
    {
        this.water = this.water - w;
    }

    private int water = 0;
}


class BrokenCup extends Cup
{
    public void addWater(int w) 
    {
        System.out.println("shit, broken cup");
    }

    public void drinkWater(int w)
    {
        System.out.println("om...num..., no water inside");
    }
}


public class Test
{
    public static void main(String[] args)
    { 
        Cup aCup;
        BrokenCup aBrokenCup = new BrokenCup();
        aCup = aBrokenCup; // 向上转换
        aCup.addWater(10); 
    }
}

输出结果:

shit, broken cup

在上面可以看到,不需要任何显示说明,我们将衍生类引用 aBrokenCup 赋予给它的基类引用 aCup。类型转换将由 Java 自动进行。

我们随后调用了 aCup (我们声明它为 Cup 类型)的 addWater() 方法。尽管 aCupCup 类型的引用,它实际上调用的是 BrokenCupaddWater() 方法!

也就是说,即使我们经过 upcast,将引用的类型宽松为其基类,Java 依然能正确的识别对象本身的类型,并调用正确的方法。Java 可以根据当前状况,识别对象的真实类型,这叫做 多态(polymorphism)。多态是面向对象的一个重要方面。

多态是 Java 的支持的一种机制,同时也是面向对象的一个重要概念。这提出了一个分类学的问题,既 子类对象实际上是父类对象。比如一只鸟,也是一个动物;一辆汽车,也必然是一个交通工具。Java 告诉我们,一个衍生类对象可以当做一个基类对象使用,而 Java 会正确的处理这种情况。

比如下面的继承关系:

我们可以说用杯子(Cup)喝水(drinkWater)。实际上,喝水这个动作具体含义会在衍生类中发生很大变换。比如用吸管喝水,和从一个破杯子喝水,这两个动作差别会很大,虽然我们抽象中都讲“喝水”。

我们当然可以针对每个衍生类分别编程,调用不同的 drinkWater 方法。然而,作为程序员,我们可以对杯子编程,调用 CupdrinkWater() 方法,而无论这个杯子是什么样的衍生类杯子。Java 会调用相应的正确方法,正如我们在上面程序中看到的。

看一个更加有意义的例子,我们给 Human 类增加一个 drink() 方法,这个方法接收一个杯子对象和一个整数作为参数。整数表示喝水的水量:

class Human 
{
    void drink(Cup aCup, int w)
    {
        aCup.drinkWater(w);
    }
}


public class Test
{
    public static void main(String[] args)
    {
        Human guest = new Human();
        BrokenCup hisCup  = new BrokenCup();
        guest.drink(hisCup, 10);
    }
}

输出结果:

om...num..., no water inside

我们在 Human 类的 drink() 的定义中,要求第一个参数为 Cup类型的引用。但在实际运用时(Test类),该参数是 CupBrokenCup 衍生类对象。

这实际上是将 hisCup 向上转型称为 Cup 类,传递给 drink() 方法。在方法中,我们调用了 drinkWater() 方法。Java 发现这个对象实际上是 BrokenCup 对象,所以实际调用了 BrokenCup 的相应方法。




downcast

我们可以将一个基类引用 向下转型(downcast)成为衍生类的引用,但要求该基类引用所指向的对象,已经是所要 downcast 的衍生类对象。比如可以将上面的 hisCup 向上转型为 Cup 类引用后,再向下转型成为 BrokenCup 类引用。




Object 类型

Java 中,所有的类实际上都有一个共同的继承祖先,即 Object 类。Object 类提供了一些方法,比如 toString()。我们可以在自己的类定义中覆盖这些方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354