多态使用缺陷

本文探讨了在Java中多态使用时的两种主要缺陷,聚焦于“覆盖”方法和调用域的问题。尽管子类可以覆盖父类的方法,但当子类对象引用转型为父类引用时,如果子类存在与父类同名的方法,实际调用的仍然是父类的方法,而非子类的覆盖方法。这表明在继承体系中,被“覆盖”的方法只能存在于基类,且与子类同名的“覆盖”方法无关。总结来说,多态引用调用的方法受限于引用类型,而非实际对象类型。

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

多态使用时的缺陷主要分两种情形

  • “覆盖”private方法
  • 调用域与静态方法

由于调用域和静态方法十分相似,先考虑调用域与“覆盖”private方法的情形。我写了个四层继承关系的程序表现这种陷阱,如下

/*
 * 继承关系:SubSubSub 继承 SubSub 继承 Sub 继承 HelloWorld
 */
public class HelloWorld {   
    public int field = 0;
    public void f(){
        System.out.println("Super.f()");
    }
    public static void main(String[] args) {
        //4种不同的父类引用
        HelloWorld fool1 = new SubSubSub();
        Sub fool2 = new SubSubSub();
        SubSub fool3 = new SubSubSub();
        SubSubSub fool4 = new SubSubSub();
        //同一子类对象对应不同引用的域
        System.out.print(fool1.field + " ");
        System.out.print(fool2.field + " ");
        System.out.print(fool3.field + " ");
        System.out.println(fool4.field + " ");
        //同一子类对象对应不同引用的方法调用
        fool1.f();
        fool2.f();
        fool3.f();
        fool4.f();
    }
}

class Sub extends HelloWorld{
    public int field = 1;
    public void f() {
        System.out.print("Sub.f()" + " ");
        System.out.print(field + " ");
    }
}

class SubSub extends Sub{
    public int field = 2;
    public void f() {
        System.out.print("SubSub.f()" + " ");
        System.out.print(field + " ");
    }
}

class SubSubSub extends SubSub{
    public int field = 3;
    public void f(){
        System.out.print("SubSubSub.f()" + " ");
        System.out.print(field + " ");
    }
}

输出结果:
0 1 2 3 
SubSubSub.f() 3 SubSubSub.f() 3 SubSubSub.f() 3 SubSubSub.f() 3 

从运行结果可知:

  • 对于域,由当前对象在继承关系中的实际引用决定。即使对象是SubSubSub类处于继承关系最下层(假设继承图中基类在顶层,子类在基类下方),若将其引用向上转型为父类引用(如fool1、fool2、fool3),那么当调用域当前引用.field时,输出的是父类的field值。
  • 方法则不同,无论有无初始化(父类构造器中调用被子类覆盖的方法),在整个继承体系(继承类、基类)中,子类的方法都会渗透到父类最顶层。无论当前对象new SubSubSub()的引用是否转型为父类引用,当调用继承体系中可继承的方法时,只会调用实际对象覆盖过的子类方法,即后期绑定。

不过要注意,向上转型为父类引用调用方法时可能会遇到一个陷阱:子类”覆盖“private方法

public class HelloWorld {   
    public int field = 0;
    //父类私有方法
    private void f(){
        System.out.println("Super.f()");
    }
    public static void main(String[] args) {
        //4种不同的父类引用
        HelloWorld fool1 = new SubSubSub();
        Sub fool2 = new SubSubSub();
        SubSub fool3 = new SubSubSub();
        SubSubSub fool4 = new SubSubSub();
        //同一子类对象对应不同引用的方法调用
        fool1.f();
        fool2.f();
        fool3.f();
        fool4.f();
    }
}

输出结果:
Super.f()
SubSubSub.f() 3 SubSubSub.f() 3 SubSubSub.f() 3 

从运行结果可知:

  • 当子类中有与基类private方法(如f())同名的 “覆盖”方法时,由于private方法无法被子类继承也就无法被覆盖,当子类对象引用转型为基类引用时,无法通过基类引用实际子类“覆盖”的方法,实际调用的是基类的private方法。因为这时父类的private方法不在继承方法体系中,和子类同名的“覆盖”方法没有任何关系。private方法相当于父类的独有方法,同理,子类同名的“覆盖”方法也是子类“独有”的方法。

注意:在一个继承体系中,被“覆盖”的private方法只能存在于基类方法中,不属于可继承方法。

总而言之,继承中覆盖只对继承体系中可继承的方法集合适用,而域独立存在于所属类,不在继承之列。

每种多态引用只能调用当前引用对应类的方法和域,跟引用实际对象(子类或基类对象,如SubSubSub类对象)的方法无关,只有继承体系内可继承的具体方法实现能调用实际对象的覆盖方法。所以实际看来,“覆盖”private方法只是一个“看似陷阱”的陷阱

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值