父子间的关系到底有多复杂-java

322 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

子类与父类之间如何实现多态?首先,多态有三个必要的条件:继承、重写、父类引用指向子类对象。在java中,多态的核心表现主要有两种:方法多态和对象多态。

方法多态

  • 方法重写

子类重写父类的方法,同一个父类的方法不同的子类可以有不同的实现

public class PenkException {
    private String message;
    public String getMessage() {
        return message;
    }
}
public class BusinessException extends PenkException {
    @Override
    public String getMessage() {
        return super.getMessage();
    }
}
  • 方法重载

同一个方法名称,通过不同的参数的类型或者不同的参数个数来调用不同的方法,代码示例如下:

public class BusinessException extends PenkException {
    public BusinessException(RetCodeEnum retCodeEnum) {
        super(retCodeEnum);
    }
    public BusinessException(String message) {
        super(message);
    }
    public BusinessException(RetCodeEnum retCodeEnum, String message) {
        super(retCodeEnum, message);
    }
    public BusinessException(RetCodeEnum retCodeEnum, String message, Errors data) {
        super(retCodeEnum, message, data);
    }
}

提供了多个构造器,名称相同,但是参数不一样,属于构造器重载

重写和重载都是实现多态的方式,区别在于重载是实现的编译时的多态性,而重写实现的是运行时的多态性,重载发生在同一个类中,而重写是发生子类与父类之间

以上为大多数文章对于方法多态的解释。个人认为重载并不是多态,重载在方法调用前就已经确定了调用某个方法,属于早绑定。多于多态而言,只有运行到调用某个方法时,解析器才能确定调用哪一个方法,这属于晚绑定。引用一句Bruce Eckel的话:“不要犯傻, 如果它不是晚绑定,它就不是多态。所以说,我认为重载并不是多态的一种提现,重载也不满足于多态的三个必要条件(继承、重写、父类引用指向子类对象)

对象多态

  • 向上转型

发生向上转型后调用的方法首先是子类重写后的父类方法,如果子类没有重写父类的方法,那还是调用的父类中的方法,传统的对象只能调用自己类中的方法,发生向上转型后,对象不仅可以调用自己类中的方法,还可以调用子类重写后的方法。 注意:向上转型不能调用子类中自己定义的方法

class A {
    public void fun1() {
        System.out.println("我是A,fun1。");
    }
    public void fun2() {
        System.out.println("我是A,fun2。");
    }
}
class B extends A {
    public void fun1() {
        System.out.println("我是B,fun1。");
    }
    public void fun3() {
        System.out.println("我是B,fun3。");
    }
}
public static void main(String[] args) {
    B b = new B();
    A a = b;
    a.fun1(); // 我是B,fun1。
    a.fun2(); // 我是A,fun2。
    a.fun3(); // 编译不通过
}
  • 向下转型

向下转型必先向上转型,因为要通过向上转型先确定子类和父类的关系,如B继承A,如果直接通过A转型为B(向下转型)的话,程序并不知道A的子类是谁(有可能A还有一个子类C,程序不知道是转为B还是转为C)。
而向上转型再向下转型,先B转型为A(java是单继承,明确的知道要转为父类A),然后A再转型为B,此时程序就可以明确的知道A和B是父子类关系,此时就可以向下转型。向下转型,可以调用父类和子类中的所有方法

class A {
    public void fun1() {
        System.out.println("我是A,fun1。");
    }
    public void fun2() {
        System.out.println("我是A,fun2。");
    }
}
class B extends A {
    public void fun1() {
        System.out.println("我是B,fun1。");
    }
    public void fun3() {
        System.out.println("我是B,fun3。");
    }
}

class C extends B {
    public void fun1() {
        System.out.println("我是C,fun1。");
    }
    public void fun4() {
        System.out.println("我是C,fun4。");
    }
}
class Test {
    public static void main(String[] args) {
        C b = new C();
        A a = b;
        B b1 = (B) a; 
        b1.fun1(); // 我是C,fun1。
        b1.fun2(); // 我是A,fun2。
        b1.fun3(); // 我是B,fun3。
    }
}