Java笔记··By/蜜汁炒酸奶

Java漫谈-协变返回类型

Java SE5中添加协变返回类型,表示在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型。 如现有:导出类WheatMill,被覆盖方法process(),基类Mill,基类方法的返回类型Grain,其导出类型Wheat。

类文件

Mill.java:基类

[toggle hide=“no” title=“基类Mill” color=“”]

/**
 * 磨坊; 工厂; 磨粉机; 榨汁机;
 */
public class Mill {
    Grain process(){
        return new Grain();
    }
}
1
2
3
4
5
6
7
8

[/toggle]

Grain.java:基类方法的返回类型

[toggle hide=“no” title=“基类方法的返回类型Grain” color=“”]

/**
 * 谷物(Grain ),可以在工厂(Mill)中被加工(process)
 */
public class Grain {
    private String name;

    @Override
    public String toString() {
        return "Grain{}---谷物";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

[/toggle]

WheatMill.java:Mill的导出类(即其子类)

[toggle hide=“no” title=“基类Mill的导出类WheatMill” color=“”]

/**
 * 工厂/磨坊(Mill)的导出类小麦磨坊(Mill)
 *
 */
public class WheatMill extends Mill {
    /**
     * 重写Mill的process()方法
     * 协变返回类型允许返回更具体的Wheat类型,亦即此时的Wheat为协变返回类型
     * @return
     */
    Wheat process(){
        return new Wheat();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

[/toggle]

Wheat.java:谷物(Grain)的导出类小麦(Wheat)

[toggle hide=“no” title=“基类Mill的导出类WheatMill” color=“”]

/**
 * 谷物(Grain)的导出类小麦(Wheat)
 */
public class Wheat extends Grain{
    private String color;

    @Override
    public String toString() {
        return "Wheat{}---小麦: " + getName() + " , the color :"+ this.color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

[/toggle]

CovariantReturnDemo.java:运行demo

[toggle hide=“no” title=“测试类CovariantReturnDemo” color=“”]

/**
*By windcoder.com
*/
public class CovariantReturnDemo {
    public static void main(String[] args) {
        // 创建基类Mill的实例m
        Mill m = new Mill();
        // 获取m的返回类型Grain的实例g
        Grain g = m.process();
        //设置g的名称并打印
        g.setName("windCoder.com");
        System.out.println(g);
        System.out.println(g.getName());
        //将m重新分配给其类型(基类Mill)的导出类(WheatMill),由于m自身是Mill,此时自动向上转型
        m = new WheatMill();
        //获取m的返回类型Grain的实例g
        g =m.process();
        g.setName("bulu");
        //! g.setColor("red");   //---此时g的类型依旧是基类Grain,故不存在其子类(Wheat)中的setColor()方法
        System.out.println(g);
        System.out.println(g.getName());
        // 因为是协变返回类型,所以可以向下转型
        Wheat w = (Wheat) g;
        w.setColor("red");
        System.out.println(g);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

[/toggle]

运行结果

Grain{}---谷物: heihei
heihei
Wheat{}---小麦: bulu , the color :null
bulu
Wheat{}---小麦: bulu , the color :red
1
2
3
4
5

解析

与较早版本差别:

较早版本将强制process()的覆盖版必须返回Grain,而不能返回Wheat。 但Wheat是从Grain导出的,因而也应该是一种合法的返回类型。 协变返回类型允许返回更具体的Wheat类型

扩:

里氏代换原则(任何基类可以出现的地方,子类一定可以出现)

重写/覆盖规则:

  • 1.重写方法不能比被重写方法限制有更严格的访问级别。
  • 2.参数列表必须与被重写方法的相同。
  • 3.返回类型必须与被重写方法的返回类型相同。
  • 4.重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。但是可以抛出更少,更有限或者不抛出异常。
  • 5.不能重写被标识为final的方法。
  • 6.如果一个方法不能被继承,则不能重写它。如private方法

可见此处demo中WheatMill对Mill的process()方法的重写违反了重写规则3,但Wheat属于Grain的子类(即Wheat IS-A Grain),所以在向上的继承树转换时会隐式完成。此处的Wheat即成了协变返回类型。

预览
Loading comments...
0 条评论

暂无数据

example
预览