多态
多态是面向对象的重要特征之一,多态主要体现在重写。
分派涉及到的概念
- 变量被声明时的类型叫做变量的静态类型(Static Type) 又叫明显类型(Apparent Type)。
- 变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)。
- 根据对象的类型而对方法进行的选择,就是分派(Dispatch)。
根据分派发生的时期,可以将分派分为两种,即分派分静态分派和动态分派。
- 静态分派(Static Dispatch)发生在编译时期,分派根据静态类型信息发生。方法重载(Overload)就是静态分派。(所谓的:编译时多态)
- 动态分派(Dynamic Dispatch)发生在运行时期,动态分派动态地置换掉某个方法。面向对象的语言利用动态分派来实现方法置换产生的多态性。(所谓的:运行时多态)
基于静态分配的例子
public static void main(String[] args) {
Human h = new Human();
h.sayHello(h);
Human m = new Man();
h.sayHello(m);
Human w = new Woman();
h.sayHello(w);
/*
* Human称为变量的静态类型,或者叫做外观类型,
* 后面的Man被称为变量的实际类型。 方法的重载
* 是通过参数的静态类型作为判断的 ,并且静态类
* 型是编译器可知的,因此在编译阶段编译器会根
* 据参数的静态类型决定使用哪个重载版本。
*/
/*
* 依赖静态类型来定位方法执行版本的分派动作称为静态
* 分派。静态分派的典型应用就是方法重载。
*
*/
/*
* 另一个问题,是编译器虽然能确定出
* 方法的重载版本,但是在很多情况下
* 这个重载版本并不是唯一的,只能确
* 定一个更加合适的版本。例如,重载
* 的方法中,参数列表除了参数类型不
* 一样,其他都一样,例接收的参数有ch
* ar\int\long等,传入参数‘a’,则会调用
* 需要char类型参数的方法,去掉需要char类型
* 参数的方法,则会调用需要int类型参数的方
* 法。这时发生了一次自动类型转换。同样,去掉需要
* int类型参数的方法,则会调用需要long类
* 型参数的方法。这里再次发生类型转换,会按照
* char->int->long->float->double转换类型。
*
*/
}
public void sayHello(Human h) {
System.out.println("Human");
}
public void sayHello(Man m) {
System.out.println("man");
}
public void sayHello(Woman w) {
System.out.println("woman");
}
}
class Man extends Human{
}
class Woman extends Human{
}
基于动态分派的例子
这里写代码片public class Human1 {
public static void main(String[] args) {
Human1 h1 = new Human1();
h1.say();
Human1 h2 = new Woman1();
h2.say();
Human1 h3 = new Man1();
h3.say();
/*
* 动态分派和重写联系密切
* Java是通过变量的实际类型在运行期确定执行哪个版本 。
* 上面3个变量的实际类型不同,会调用各自的方法。
*/
}
public void say() {
System.out.println("human");
}
}
class Man1 extends Human1{
@Override
public void say() {
// TODO Auto-generated method stub
System.out.println("man");
}
}
class Woman1 extends Human1{
@Override
public void say() {
// TODO Auto-generated method stub
System.out.println("woman");
}
}
基于动态分配的在接口中也同样适用:
public interface Tech {
void tech();
}
public class TechMath implements Tech{
@Override
public void tech() {
// TODO Auto-generated method stub
System.out.println("我教数学");
}
}
public class TechChinese implements Tech{
@Override
public void tech() {
// TODO Auto-generated method stub
System.out.println("我是教语文");
}
public static void main(String[] args) {
Tech t = new TechChinese();
t.tech();
Tech t1 = new TechMath();
t1.tech();
}
}
面试题
最后看一个例子:
public class Test276 {
public static void main(String[] args) {
Collection<?>[] collections = {new HashSet<String>(),new ArrayList<String>(),new HashMap<String,String>().values()};
Super s = new sub();
for(Collection<?> c:collections) {
System.out.println(s.getType(c));
}
}
abstract static class Super{
public static String getType(Collection<?> c) {
return "super collection";
}
public static String getType(List<?> c) {
return "super list";
}
public static String getType(ArrayList<?> c) {
return "super arraylist";
}
public static String getType(Set<?> c) {
return "super set";
}
public static String getType(HashSet<?> c) {
return "super hashset";
}
}
static class sub extends Super{
public static String getType(Collection<?> collection) {
return "Sub"; }
}
}
执行结果:super collection 打印3次
本例中,如果去掉static,那么本例就是打印子类的方法。如果子类参数类型不符合,就往父类找,就近找,父类找不到,就报错。