Overload 与 Override的说明

Posted by 小马哥 on 03-29,2020

Overload 与 Override

单一分派

class Parent {
  void print(Object a) { log.info("Parent - Object"); }
}
class Child extends Parent {
  void print(String a) { log.info("Child - String"); }
}

String string = "";
Parent parent = new Child();
parent.print(string);// Prints: "Parent - Object"

print(Object a)print(String a) 并没有进行Override ,匹配规则声明 > 实际引用

Java 会检查 Parent::print(Object) 有没有潜在的 Override 方法,结果没有找到。因此,最后执行的就是这个方法

参数的“声明”类型优先于“实际”类型

多重继承:类与接口

class ParentClass {
  void print() { log.info("I am a class!"); }
}
interface ParentInterface {
  default void print() { log.info("I am an interface!"); }
}
class Child extends ParentClass implements ParentInterface {}

new Child().print();  // Prints: "I am a class!"

如果类和接口在继承时发生冲突,继承类优先

私有 Override

class Parent {
  void print() { foo(); }
  private void foo() { log.info("I am Parent!"); }
}
class Child extends Parent {
  void foo() { log.info("I am Child!"); }
}

new Child().print();  // Prints: "I am Parent!"

现在 Parent.foo() 声明变为 private,除此之外,这个示例与前面的例子完全相同。无论 foo() 在子类中是否实现了其它版本,也不管调用 print() 的实例类型如何,当 Parent.print() 调用 foo()时,foo() 都会硬编码为 Parent.foo()

通常认为,把一个方法从 public 改为private,只要编译成功,仅仅是一次重构。上面的例子证明这种想法是错的:即使编译成功,系统行为也可能出现意料之外的变化。

解决方法,可以为方法加上 @Override 注解。一旦改变基类方法的可见性,就会引起编译错误。

静态 Override

class Parent {
  static void print() { log.info("I am Parent!"); }
}
class Child extends Parent {
  static void print() { log.info("I am Child!"); }
}

Child child = new Child();
Parent parent = child;
parent.print(); // Prints: "I am Parent!"
child.print();  // Prints: "I am Child!"

Java 不支持静态方法 Override。如果父类和子类中定义了相同的静态方法,仅根据声明类型决定调用哪个方法,不考虑实例类型

这与非静态方法的处理完全相反:非静态方法会依据实例类型决定调用哪个方法,忽略声明类型。因此,把方法改为静态方法或者反向操作需要格外小心。即使没有编译错误,系统行为也可能出现意料之外的变化。

这也是给方法加上 @Override 注解的另一个重要原因。上面的例子中,如果为 Child::print 加上注解会报告一个编译错误:静态方法不能 Override