Java上溯造型与动态绑定

1.上溯造型

1.1 什么是上溯造型

以代码为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Instrument{
public void play(){
System.out.println("play()");
}
static void tune(Instrument i){
i.play();
}
}
public class Wind extends Instrument{
public static void main(String[] args) {
Wind flute = new Wind();
//一个Wind对象也是一个Instrument对象
//从Wind类型的句柄flute转换为Instrument类型,这就是造型上溯
Instrument.tune(flute); //Upcasting
}
}

上溯造型:将子类句柄转化为父类类型

此时类的衍生关系可表示为:

image.png

1.2 为什么要使用上溯造型

1.2.1 不使用上溯造型

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
39
40
41
42
43
44
45
46
47
class Note2 {
private int value;
private Note2(int val) { value = val; }
public static final Note2
middleC = new Note2(0),
cSharp = new Note2(1),
cFlat = new Note2(2);
} // Etc.
class Instrument2 {
public void play(Note2 n) {
System.out.println("Instrument2.play()");
}
}
class Wind2 extends Instrument2 {
public void play(Note2 n) {
System.out.println("Wind2.play()");
}
}
class Stringed2 extends Instrument2 {
public void play(Note2 n) {
System.out.println("Stringed2.play()");
}
}
class Brass2 extends Instrument2 {
public void play(Note2 n) {
System.out.println("Brass2.play()");
}
}
public class Music2 {
public static void tune(Wind2 i) {
i.play(Note2.middleC);
}
public static void tune(Stringed2 i) {
i.play(Note2.middleC);
}
public static void tune(Brass2 i) {
i.play(Note2.middleC);
}
public static void main(String[] args) {
Wind2 flute = new Wind2();
Stringed2 violin = new Stringed2();
Brass2 frenchHorn = new Brass2();
tune(flute); // No upcasting
tune(violin);
tune(frenchHorn);
}
}

经过观察,我们可以发现:不使用上溯造型时,我们需要为不同的子类定制接口,并重载父类方法,但他们的功能却大致相同,这使得我们的工作量巨大

1.2.2 使用上溯造型

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
class Note{
private int value;
private Note(int val){value = val;}
public static final Note middleC = new Note(0),
cSharp = new Note(1), cFlat = new Note(2);
}
class Instrucment{
public void play(Note n){
System.out.println("Instrucment.play()");
}
}
class Wind extends Instrucment{
public void play(Note n){
System.out.println("Wind.play()");
}
}
public class Music {
public static void tune(Instrucment i){
i.play(Note.middleC);
}

public static void main(String[] args) {
Wind flute = new Wind();
tune(flute);
}
}

使用上溯造型,可以不管子类,而直接和父类交互,从而避免了大量重复代码,简少工作量,提高了代码的重用。

1.3怎么上溯造型

正如定义所诉,不再赘述。

这里不禁有个疑问,父类是如何知道我们传入的是哪种子类对象,从而有不同的响应呢?接下来就引入动态绑定的概念:

(这里只是根据书中的解释粗浅的理解,挖个坑,以后再来填~~)

2.动态绑定

绑定:将方法调用与方法主体连接在一起

  • 早期绑定:在程序运行以前执行绑定(C语言)
  • 后期绑定(也叫运行期绑定、动态绑定):提供某种机制,在运行期间判断对象的类型,调用适当的方法

2.1 什么是动态绑定

提供某种机制,在运行期间判断对象的类型,调用适当的方法

2.2 为什么使用动态绑定

我想这里应该很清晰了,上溯造型只是为我们提供了一种简单而巧妙地方法,通过子类对象上溯父类类型,然而这并不是一个完整的过程,找到正确的父类类型后,还应该产生正确的相应,而动态绑定正是在此时发挥了作用

2.3 怎么使用动态绑定

动态绑定是由虚拟机代替我们实现的,在上溯造型并获得正确的响应后,动态绑定就体现在这一过程中,所以我们只需要理解其原理,弄清楚虚拟机是如何做到调用正确的方法即可。

(这个坑以后来填)