Fork me on GitHub

Java编程思想———多态

多态也称作动态绑定、后期绑定或运行时绑定。

方法调用绑定

将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定,叫做前期绑定。在运行时根据对象的类型进行绑定叫做后期绑定。后期绑定也叫做动态绑定或运行时绑定。

Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。

缺陷:“覆盖”私有方法

只有非private方法才可以被覆盖;但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行。确切地说,在导出类(子类)中,对于基类中的private方法,最好采用不同的名字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class PrivateOverride {
private void f() {
System.out.println("private f()");
}
public static void main(String[] args) {
PrivateOverride po = new Derived();
po.f();
}
}

class Derived extends PrivateOverride {
public void f() {
System.out.println("public f()");
}
}

//output
private f()

缺陷: 域与静态方法

任何域访问操作都将由编译器解析,因此不是多态的。

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
28
29
class Super {
public int field = 0;
public int getField() {
return field;
}
}

class Sub extends Super {
public int field = 1;
public int getField() {
return field;
}
public int getSuperField() {
return super.field;
}
}

public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();
System.out.println("sup field = " + sup.field + ", sup.getField() = " + sup.getField());
Sub sub = new Sub();
System.out.println("sub field = " + sub.field + ", sub.getField() = " + sub.getField() + ", sub.getSuperField() = " + sub.getSuperField());
}
}

//output
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0

只有普通的方法调用可以是多态的,如果某个方法是静态的,它的行为就不具有多态性。

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
28
29
class StaticSuper {
public static String staticGet() {
return "Base staticGet()";
}
public String dynamicGet() {
return "Base dynamicGet()";
}
}

class StaticSub extends StaticSuper {
public static String staticget() {
return "Derived staticGet()";
}
public String dynamicGet() {
return "Derived dynamicGet()";
}
}

public class StaticPolymorphism {
public static void main(String[] args) {
StaticSuper sup = new StaticSub();
System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
}

//output
Base staticGet()
Derived dynamicGet()

构造器内部的多态方法的行为

在一般的方法内部,动态绑定的调用是在运行时才决定的,因此对象无法知道它是属于方法所在的那个类,还是属于那个类的导出类。

如果要调用构造器内部的一个动态绑定方法,就要用到那个方法的被覆盖后的定义。

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
28
29
30
31
32
33
class Glyph {
void draw() {
System.out.println("Glyph.draw()")
}
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}

class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println("RoundGlyph.RoundGlyph(). radius = " + radius;
}
void draw() {
System.out.println("RoundGlyph.draw(). radius = " + radius;
}
}

public class PolyConstructors {
public static void main(String[] args){
new RoundGlyph(5);
}
}

//output
Glyph() before draw()
RoundGlyph.draw(). radius = 0
Glyph() after draw()
RoundGlyph.draw(). radius = 1

编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法”。
在构造器内唯一能够安全调用的那些方法是基类中的final方法(也适用于private方法,它们自动属于final方法)。这些方法不能被覆盖,因此也就不会出现令人惊讶的问题。

协变返回类型

协变返回类型指的是子类中的成员函数的返回值类型不必严格等同于父类中被重写的成员函数的返回值类型,而可以是更 “狭窄” 的类型。
Java 5.0添加了对协变返回类型的支持,即子类覆盖基类方法时,返回的类型可以是基类方法返回类型的子类。协变返回类型允许返回更为具体的类型。

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
28
29
30
31
32
33
34
35
36
37
38
class Grain {
public String toString() {
return "Grain";
}
}

class Wheat extends Grain {
public String toString() {
return "Wheat";
}
}

class Mill {
Grain process() {
return new Grain();
}
}

class WheatMill extends Mill {
Wheat process() {
return new Wheat();
}
}

public class CovariantReturn {
public static void main(String[] args) {
Mill m = new Mill();
Grain g = m,process();
System.out.println(g);
m = new WheatMill();
g = m.process();
System.out.println(g);
}
}

//output
Grain
Wheat
求鼓励,求支持!