多态是OOP的另一个特点。是指一个名字有多种形态。
在java里面有两种多态:
- compile-time polymorphism — method overloading
- runtime polymorphism — method overriding
在java里的多态是用method overloading 和 method overriding来实现的。简单点来说就是:
用同一个方法的名字做了不同的事情
1.Method Overloading in Java
2.Method Overriding in Java
3. Overloading vs. Overriding
1. Method Overloading in Java
同一个类里面,允许有多个方法用同一个名字,但是不同的参数。 在 知识点3 和 知识点4 里面讲过constructor overloading 。
不同参数的意思:
- 个数不同
- 参数类型不同
- 参数顺序不同
例子:
int add(int a, int b)
int add(int a, int b, int c)
int add(int a, float b)
int add(float a, int a)
注意:
只要parameters不同,返回值可以相同也可以不同,throws exception也不重要, public还是protected也不重要,是不是static的也不重要。
如果, 其中一个参数是另一个参数的子类算不算?
答案: 可以的!那如果传一个子类进去,调用哪个方法? 看例子:
public class OverloadTest {
public String m1(A a) {
return "first, para 'a' get";
}
public String m1(B b) {
return "second, para 'b' get";
}
public static void main(String args[]) {
OverloadTest ot = new OverloadTest();
A a1 = new A();
System.out.println(ot.m1(a1)); // first, para 'a' get
B b = new B();
System.out.println(ot.m1(b));// second, para 'b' get
A a2 = new B();
System.out.println(ot.m1(a2));// first, para 'a' get
}
}
class A{}
class B extends A{} //b是a的子类
最后,关于static 关键词,其实不重要
static int add(int a, int b)
int add(int a, int b) //不行的,Compiler Error: cannot redefine add
最后的最后,overload是同一个类里面同样的方法名字不同的方法,但是方法可以是自己的,也可以是从父类那里继承来的。
2. Method Overriding in Java
关于override,已经在继承那里讲了好多了。继续详细讲一下怎么通过override实现runtime的多态的。
Runtime polymorphism 也可以看作是 Dynamic Method Dispatch 。 顾名思义就是在父类和子类里面有同样的方法,同样的参数,在runtime的时候才知道到底会调用哪一个。
对于overload,执行哪一个方法其实是显而易见的,数一数参数,哪一个方法对应上了就执行哪一个。但是在override的时候,尤其是reference的类型是实例类型的父类的时候(upcasting),这个就不太明显了,因为参数也是一样的。
看下面代码:
class Animal{}
class Cat extends Animal{}
Animal tom=new Cat(); // upcasting
这个时候tom是一只猫,tom也是一只动物。tom这个名字的类型是Animal, 这个名字它指向的是一只猫。
这里主要讨论在override发生的时候,到底执行的是哪个类里的方法定义。
class Animal{
void eat(){System.out.println("eating...");}
}
class Dog extends Animal{
void eat(){System.out.println("dog eatting");}
}
class Poodle extends Dog{
void eat(){System.out.println("Poodle eating");}
}
public class TestPolymorphism3{
public static void main(String[] args){
Animal a;
a=new Dog();
a.eat();
a=new Poodle();
a.eat();
}
}
输出:
dog eatting
Poodle eating
所以,reference的type不重要,执行的是实际的object的class里的方法。
来个大招:
class Animal{
void eat(){System.out.println("eating...");}
}
class Dog extends Animal{
void eat(){System.out.println("dog eatting");}
}
class Poodle extends Dog{
void eat(){System.out.println("Poodle eating");}
}
public class TestPolymorphism3{
public static void main(String[] args){
Animal a = new Poodle();
a.eat();
Dog d = (Dog)a;
d.eat();
Poodle p= (Poodle)d;
p.eat();
}
}
结果:
Poodle eating
Poodle eating
Poodle eating
由此可见,reference类型不重要,类型转化也不重要,执行哪个方法,只看被谁new出来的
3. Overloading vs. Overriding
Overloading | Overriding |
---|---|
同一个class里面用同一个方法名字不同的方法实现 | 给从父辈继承来的方法提供不同的方法实现 |
可以是同一个class也可以是在父子类里面 | 只发生在父子类里面 |
方法的参数不同 | 方法的参数必须相同 |
可以有不同返回类型 | 返回类型必须相同或者子类的返回类型是父类的返回类型的子类 |
access modifier 随意 | 子类的access level 只能越来越大 |
throws出来的exception 随意 | 子类抛出的异常需要是父类抛出异常的孩子,或者是抛出比父类少的异常 |