子类中隐藏父类方法而非废弃,是否有替代重写的更好方案?
好问题!咱们先把Java里的核心规则说清楚:子类继承父类的public方法后,没办法真正“隐藏”它——毕竟Java的继承机制里,public成员是对子类可见的,而且你还不能把它的访问权限降低(比如改成private,编译器直接报错)。不过咱们有几种方案能达到「让调用者没法轻易使用,或者使用时立刻明确知道不该用」的效果,其中有些比重写更优雅,一起来看:
1. 用接口隔离原则重构代码(最推荐)
这其实是从设计根源解决问题的方案,符合SOLID原则。既然BarTwo不需要aMethod(),说明原来的抽象父类Foo职责太宽泛了。我们可以拆分出专门的接口,把不同职责分开:
// 拆分后的抽象父类,只保留所有子类都需要的通用方法 public abstract class Foo { // 这里放BarOne和BarTwo都需要的方法 } // 定义专门的接口,只包含aMethod() public interface SupportsAMethod { void aMethod(); } // BarOne继承Foo并实现接口,自然拥有aMethod() public class BarOne extends Foo implements SupportsAMethod { @Override public void aMethod() { // 可以直接复用原来Foo的实现逻辑,或者自己扩展 super.aMethod(); // 如果Foo里还保留该方法的私有实现,或者直接写逻辑 } } // BarTwo只继承Foo,完全不碰SupportsAMethod接口 public class BarTwo extends Foo { // 这里只有Foo的通用方法,根本不存在aMethod() }
这样一来,BarTwo的类型里就没有aMethod(),调用者从编译阶段就没法调用这个方法,彻底解决了隐私问题,比重写要健壮得多。
2. 重写+final+抛出异常(无法重构父类时的次优解)
如果因为历史代码限制,没法修改父类Foo,那重写是绕不开的,但我们可以优化得更安全:把重写的方法设为final,防止子类再篡改,同时抛出明确的异常,让调用者立刻知道这个方法不可用——比空实现强太多,空实现会让调用者误以为方法正常,埋下隐患。
public class BarTwo extends Foo { /** * 该方法因隐私需求在BarTwo中不可用,禁止调用 * @throws UnsupportedOperationException 调用此方法时抛出该异常 */ @Override public final void aMethod() { throw new UnsupportedOperationException("BarTwo does not support aMethod due to privacy restrictions"); } }
3. 修改父类访问权限(父类可修改时的备选方案)
如果你有权限修改父类Foo,可以把aMethod()的访问权限改成protected,这样外部调用者就看不到这个方法了。然后让BarOne自己把方法暴露为public,BarTwo则完全不对外暴露:
// 修改后的父类Foo public abstract class Foo { protected void aMethod() { // 把public改成protected // 原有代码逻辑 } } // BarOne主动把方法暴露为public public class BarOne extends Foo { @Override public void aMethod() { super.aMethod(); } } // BarTwo继承Foo,但不重写aMethod(),外部看不到这个方法 public class BarTwo extends Foo { // 外部调用者无法访问aMethod() }
这个方案能真正让BarTwo的外部调用者看不到aMethod(),但前提是你能修改父类的代码。
总结一下:优先选接口隔离重构,这是最优雅的设计层面解决方案;如果没法动父类,就用final+抛出异常的重写方式;修改父类访问权限是备选,适合父类可修改的场景。
内容的提问来源于stack exchange,提问作者Aperture Prometheus

