继承怎么做?Java和Python的写法加避坑经验

继承怎么做?Java和Python的写法加避坑经验-苏州z35资讯网

时尚 阅读

经常有人在群里问继承怎么做,我一般先反问一句:你是想问语法,还是想问该不该继承。这两个问题难度差得挺远。语法十分钟就能学会,判断该不该用继承,我写了几年代码才摸出点门道。下面把两部分都讲一遍,语法部分给 Java 和 Python 的写法,后半段说判断标准。

语法层面:Java 和 Python 怎么写

Java 里用 extends。先定义一个父类 Animal,里面有个 speak() 方法,然后写 class Dog extends Animal,Dog 就自动拥有了 speak()。想改掉父类的行为,在子类里重新写一遍同名方法,上面加个 @Override 注解,编译期能帮你检查有没有写错签名。子类构造器的第一行用 super(...) 调父类构造器,你不写的话编译器会自动插一个无参的 super(),如果父类压根没有无参构造器,这一行就会直接报错,这个坑我踩过不止一次。

继承怎么做?Java和Python的写法加避坑经验

Python 的写法是把父类写在类名后面的括号里,class Dog(Animal): 这样。构造器里第一句要手动写 super().__init__(name),注意是手动,不写的话父类的属性根本不会初始化,等到调用某个方法的时候才报 AttributeError,排查起来很烦。Python 还支持多继承,class C(A, B) 这种,方法查找顺序按 MRO 走,C3 算法算出来的那条链,写起来自由,但父子关系一乱就不好维护,我在项目里见过四层多继承的结构,最后没人敢动。

继承怎么做?Java和Python的写法加避坑经验

另外说一句重写。Java 里字段不存在重写,只有方法重写,子类声明同名字段是把父类的字段藏起来,两个字段同时存在,取值看引用类型,这点很多人搞混。Python 就更随意,属性直接覆盖,父类的值想留就自己存一份。

继承怎么做?Java和Python的写法加避坑经验

真正难的是判断该不该继承

语法会了之后,麻烦才刚开始。继承是一种强耦合,父类改一个方法,所有子类的行为都可能跟着变。我判断的标准大概是这几条:

  • 是不是严格的 is-a 关系。Dog 是 Animal,成立;OrderService 是 BaseService,这个通常不成立,只是你想复用代码而已。
  • 子类需不需要父类的全部行为。如果只用到一两个方法,剩下全是摆设,那说明继承用错了。
  • 父类的改动会不会波及子类。会波及,就要想清楚这个代价能不能接受。
  • 单纯为了复用代码的,优先考虑组合,把对象作为成员变量塞进去,调用它的方法就行。

去年我接手过一个订单模块,继承链是这样的:OrderService 继承 BaseService,BaseService 继承 CommonService,CommonService 继承 AbstractHandler。四层下来,想找 createOrder 这个方法到底在哪实现,得往上翻三个文件。后来有个需求要改 CommonService 里的日志逻辑,我改完之后,支付模块和退款模块的行为全变了,测试跑了一整天才发现是这条继承链惹的祸。重构的时候把 CommonService 拆成了两个工具类,用组合的方式注入进去,继承链压到一层,代码量反而少了。

我的做法是,写新类之前先问自己一句:这个东西是不是父类的一种。答案含糊,就老老实实用组合。接口加组合能解决大部分场景,继承留给那些关系明确、行为共享稳定、父子同步演进的情况。

最后提一点实践建议。父类尽量写成抽象类,把必须实现的方法标成 abstract,逼子类去实现;父类里的方法能不 protected 就不 protected,公开得越多,子类越容易依赖你的内部实现;构造函数里不要调用可被重写的方法,Java 和 Python 都有这个问题,父类构造器执行的时候子类字段还没初始化完,调用重写方法拿到的是一堆空值。这几点做到了,继承用起来才踏实。

相关阅读

更多时尚

继续翻阅

更多内容