多态
多态是面向对象编程语言中的重要特征,它允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定,从而提供了另一维度的接口与实现分离。
Java中方法调用有两类,动态方法调用和静态方法调用。而多态就是属于动态方法调用。
方法调用绑定
将一个方法调用和方法主体关联起来称作绑定。若绑定发生在程序运行前,叫做前期绑定。当父类引用指向子类对象,或形参为父类对象但实参为子类对象时,编译器就无法得知具体调用的是哪个方法。此时就引入了动态绑定。结果就是编译器仍然不知道对象的类型,但是方法调用机制能找到正确的方法并调用。
我们知道final关键字可以防止方法被重写,也因此它关闭了“动态绑定”,通常情况下Java中出了static和final方法,其它所有的方法都是动态绑定。
重载
重载不完全属于多态,和多态的概念之间有交叉。
在Java中重载一个方法,除了名字要与原方法相同以外,还要有一个不同的方法签名,签名是方法参数在常量表中的引用集合。
特别注意
- List
与 List 在泛型擦除后,都是 List,是相同参数; - 在class文件中,返回值是方法签名的一部分,返回值不同,可以共存于一个class中。
泛型
泛型的本质是为了参数化类型,在泛型的使用过程中,操作的数据类型被指定为一个参数,这种参数类型何以用在类、接口和方法中。
泛型的意义在于编写更通用的代码。
泛型擦除
如果把C++中模板看作真整的泛型,那Java的泛型则是不彻底的。Java设计之初并没有考虑泛型,在1.5以后才引入了泛型概念,为了兼容性,Java采用了擦除实现泛型。
基于擦除的实现中,泛型类型只有在静态检测期间才出现,在此之后,Java中所有的泛型都会被替换为非泛型上界。
泛型擦除与桥方法
由于泛型信息在编译时会被擦除,在多态的实现中,方法绑定会出现问题,观察下面的代码示例:
class Holder<T>{
private T value;
public T get(){
return value;
}
public void set(T value){
this.value = value;
}
}
class Simple{
}
class SimpleHolder extends Holder<Simple>{
@Override
public Simple get() {
return super.get();
}
@Override
public void set(Simple value) {
super.set(value);
}
}
对于Holder类的泛型参数在类型擦除后都会变成Object
class Holder<Object>{
private Object value;
public Object get(){
return value;
}
public void set(Object value){
this.value = value;
}
}
而在子类 SimpleHolder
中的 get/set 方法的参数却是Simple,方法的签名已经不匹配。为了解决这个问题,维持泛型类型的多态性,java编译器会生成一个桥方法:
class SimpleHolder extends Holder<Simple>{
public Object get() {
return get();
}
public void set(Object value) {
set((Simple)value);
}
}