Groovy中如何仅重写父类的构造方法?
嘿,我来给你把这个问题掰扯明白!你遇到的报错和IntelliJ的提示,本质上都是因为继承关系里构造器的调用规则在起作用,咱们一步步拆解:
为什么会报错?
首先要记住一个关键规则:在Groovy(其实底层和Java一致)中,子类的构造器必须先调用父类的某个构造器——要么是你显式写代码调用,要么是编译器自动帮你调用父类的无参构造器。
你写的Foo类只定义了一个带两个int参数的构造器,这时候Java/Groovy会自动移除默认的无参构造器(就是那种不带任何参数的Foo(){})。而你的Bar构造器里没有显式写super(a, b)来调用父类的带参构造器,编译器就会默认帮你加一句super()——但这时候Foo根本没有无参构造器,所以就抛出了NoSuchMethodError,IntelliJ提前提示你“找不到默认构造器”也是这个原因。
你加无参构造器后为什么能运行?
当你给Foo加上Foo(){}之后,编译器自动插入的super()就有对应的构造器可以调用了,所以不会报错。但这里要注意:这时候父类的a和b是用默认值初始化的,然后你在Bar里给this.a和this.b赋值,其实是覆盖了父类的属性值,但父类原来的构造器逻辑(比如println("I'm a Foo!"))并没有执行——这可能不是你真正想要的“重写构造器”的效果。
正确的做法:显式调用父类构造器
如果你想保留父类构造器的部分逻辑,同时自定义子类的构造行为,应该显式调用父类的带参构造器,比如:
class Foo { def a def b Foo(int a, int b) { this.a = a this.b = b println("I'm a Foo!") } } class Bar extends Foo { Bar(int a, int b) { super(a, b) // 显式调用父类的带参构造器,先完成父类的初始化 this.a = 2*a // 再自定义子类的属性赋值 this.b = 2*b println("I'm a Bar!") } } def f = new Foo(1, 2) def b = new Bar(1, 2)
这样运行的话,输出会是:
I'm a Foo! I'm a Foo! I'm a Bar!
因为先调用了父类的构造器执行它的逻辑,再执行子类自己的逻辑。
如果你完全不想执行父类构造器的逻辑,那其实不能直接“重写”构造器(构造器不能被重写,只能被重载或调用),这时候你可以在父类加无参构造器,然后子类构造器里只写自己的逻辑——但要清楚这时候父类的初始化逻辑不会被执行。
总结一下核心点
- 子类构造器必须调用父类的某个构造器,显式用
super(...)或者隐式调用无参super() - 父类如果定义了自定义构造器,默认无参构造器会被移除,需要手动添加才会存在
- 想要自定义子类构造逻辑同时正确继承父类,优先选择显式调用父类对应的构造器
备注:内容来源于stack exchange,提问作者Chris Curvey

