Java多态

本文围绕Java多态展开,介绍了多态可消除类型间耦合关系。阐述向上转型,即把对象引用视为基类引用,还提到可忘记对象类型以减少代码。探讨编译器运行子类方法的机制,涉及前期和后期绑定,指出Java除static和final方法外多为动态绑定,还提及构造器调用和协变返回值类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在面向对象的程序设计语言中,多态是数据抽象和继承值后的第三种基本特征

封装通过合并特征和行为来创建新的数据类型,实现隐藏则通过将细节私有化把接口和实现分离开,而多态的作用则是消除类型之间的耦合关系。

1 再论向上转型

对象既可以作为它本身的类型使用也可以作为它的父类的类型使用。这种把某个对象的引用视为对其基类的引用的做法被称为向上转型。因为子类继承自父类,具有父类的所有方法,所以父类可以进行的操作子类一定可以进行并且不需要任何的类型转化。

1 忘记对象类型

在进行多态时,我们可以先选择性的忘记对象类型问题,将导出类的对象看作基类的对象来使用,可以减少许多不必要的代码。

class Stringed extends Instrument {

public void play(Note n) {

print("Stringed.play() " + n);

}

}



class Brass extends Instrument {

public void play(Note n) {

print("Brass.play() " + n);

}

}



public class Music2 {

public static void tune(Wind i) {

i.play(Note.MIDDLE_C);

}

public static void tune(Stringed i) {

i.play(Note.MIDDLE_C);

}

public static void tune(Brass i) {

i.play(Note.MIDDLE_C);

}

public static void main(String[] args) {

Wind flute = new Wind();

Stringed violin = new Stringed();

Brass frenchHorn = new Brass();

tune(flute); // No upcasting

tune(violin);

tune(frenchHorn);

}

} /* Output:

Wind.play() MIDDLE_C

Stringed.play() MIDDLE_C

Brass.play() MIDDLE_C

*///:~

这段代码与下边的代码效果基本是相同的

public class Music {

public static void tune(Instrument i) {

// ...

i.play(Note.MIDDLE_C);

}

public static void main(String[] args) {

Wind flute = new Wind();

tune(flute); // Upcasting

}

} /* Output:

Wind.play() MIDDLE_C

*///:~

这说明:使用多态时可以很大程度的减少代码的数量,免去了为每个导出类都编写一个相关方法代码。

public class Test {

public static void main(String[] args) {

B b = new B();

C c = new C();

D d = new D();

ride(b);

ride(c);

ride(d);

}

public static void ride(A a){

a.x();

}

}

class A{

public void x(){

System.out.println("A.x");

}

}

class B extends A {

public void x(){

System.out.println("B.x");

}

}

class C extends A {

public void x(){

System.out.println("C.x");

}

}

class D extends A {

public void x(){

System.out.println("D.x");

}

} //输出结果B.x C.x D.x

2 转机

编译器是如何在接受一个父类对象的情况下准确的运行对应的子类方法的。

1 方法调用绑定

前期绑定:在程序执行前进行绑定。

而如果使用前期绑定,编译器将不知道调用哪个方法,所以多态使用后期绑定。

后期绑定:在运行时根据对象的类型进行绑定。必须在对象内安置某种“类型信息”。

Java中除了static和final方法外,其他方法都是动态绑定。final方法可以关闭动态绑定。

public class Test {

public static void main(String[] args) {

A b = new B();

b.s();

}

}

class A{

public void s(){this.x();}

public void x(){

System.out.println("A.x");

}

}

class B extends A {

public void x(){

System.out.println("B.x");

}

}

//B.x





public class Test {

public static void main(String[] args) {

Test b = new B();

b.x();

}

private void x(){

System.out.println("A.x");

}

}

class B extends Test {

public void x(){

System.out.println("B.x");

}

}//因为父类的方法是private的,所以无法进行覆盖。向上转型后调用的是父类的方法。

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(); // Upcast

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

*///:~

当将Sub转型为Super时,对于域的访问都由编译器解析,因此不是多态的,Sub实际上包含两个field域。

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(); // Upcast

System.out.println(sup.staticGet());

System.out.println(sup.dynamicGet());

}

如果某个方法是静态的就不具备多态。



class Meal {

Meal() { print("Meal()"); }

}



class Bread {

Bread() { print("Bread()"); }

}



class Cheese {

Cheese() { print("Cheese()"); }

}



class Lettuce {

Lettuce() { print("Lettuce()"); }

}



class Lunch extends Meal {

Lunch() { print("Lunch()"); }

}



class PortableLunch extends Lunch {

PortableLunch() { print("PortableLunch()");}

}



public class Sandwich extends PortableLunch {

private Bread b = new Bread();

private Cheese c = new Cheese();

private Lettuce l = new Lettuce();

public Sandwich() { print("Sandwich()"); }

public static void main(String[] args) {

new Sandwich();

}

} /* Output:

Meal()

Lunch()

PortableLunch()

Bread()

Cheese()

Lettuce()

Sandwich()

*///:~

构造器的调用:首先在main创建一个对象时,先调用父类的构造器,之后按声明顺序调用成员的初始化方法,最后调用导出类构造器的主体。

协变返回值类型

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);

}

}

Java se5开始允许覆写版本返回值为原类的导出类。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值