Java 中为什么要用 this(...) 调用其他构造函数?
使用 this(...) 调用同一个类的其他构造函数,可以复用初始化逻辑,让不同的创建方式遵循一致的初始化规则。并非每个类都需要这样写。
你的 Pay 示例还涉及 this,它是对当前对象的引用。this(...) 则用于构造函数委托。
为什么构造函数需要调用另一个构造函数?
题目中的 Foo 有两个构造函数:无参数版本提供默认值,带参数版本处理实际的初始化工作。
可以把它补充成下面这个例子:
class Foo {
private final String bar;
public Foo() {
this("Some default value for bar");
}
public Foo(String bar) {
if (bar == null) {
throw new IllegalArgumentException("bar must not be null");
}
this.bar = bar;
}
public String getBar() {
return bar;
}
}
调用者可以选择:
Foo defaultFoo = new Foo();
Foo customFoo = new Foo("Custom value");
执行 new Foo() 时,无参数构造函数通过 this("Some default value for bar") 委托给带参数构造函数,由后者完成参数检查和字段赋值。
两个构造函数作用于同一个正在创建的对象,this(...) 不会额外创建一个 Foo 对象。
这样写还有一个好处:如果以后修改初始化规则,只需维护一处。如果每个构造函数都复制检查和赋值代码,修改时就容易遗漏某个入口。
有哪些实际使用场景?
一个常见场景是提供默认配置,同时允许调用者自定义。例如,下面的重试选项类允许省略重试次数。默认值 3 只是示例中自行约定的值:
class RetryOptions {
private final int maxRetries;
public RetryOptions() {
this(3);
}
public RetryOptions(int maxRetries) {
if (maxRetries < 0) {
throw new IllegalArgumentException(
"maxRetries must not be negative"
);
}
this.maxRetries = maxRetries;
}
}
调用者可以使用默认配置,也可以指定重试次数:
RetryOptions defaults = new RetryOptions();
RetryOptions custom = new RetryOptions(5);
默认配置和自定义配置都经过同一套检查。类似的安排也适用于带默认尺寸、默认名称或可选配置的对象。
题目中的注释还说明,无参数构造函数可以在被调用的构造函数完成后继续执行代码。如果它只负责提供默认参数,调用 this(...) 后也可以没有其他语句。
Pay 示例为什么不属于构造函数委托?
你的代码中只有一个 Pay 构造函数:
public Pay(double p)
{
pay = p;
}
因此,当前没有另一个 Pay 构造函数可供委托,也没有必须引入 this(...) 的理由。
下面这句:
Overtime ot = new Overtime(x);
创建了另一个类的对象,并调用 Overtime 的构造函数。这与在 Pay 构造函数中使用 this(...) 调用其他 Pay 构造函数是不同的操作。
另外,原代码写的是:
myPay.calculatePayWithOvertime(MyPay);
Java 区分大小写。按照贴出的完整代码,MyPay 没有声明,需要改成 myPay 才能编译。
如何避免把 myPay 再传给自己?
如果这个方法的目的就是更新当前对象的 pay,可以删除参数,在方法内部使用 this:
public void calculatePayWithOvertime() {
Overtime ot = new Overtime(this);
pay = ot.getOvertimePay();
}
调用时写成:
Pay myPay = new Pay(100.0);
myPay.calculatePayWithOvertime();
System.out.println(myPay.getPay());
在这次调用中,方法里的 this 就是 myPay 所引用的对象,new Overtime(this) 把当前对象传给新建的 Overtime 对象。按示例中的计算规则,调用一次后结果为 150.0。
删除参数也缩小了方法的用途。原方法允许这样调用:
first.calculatePayWithOvertime(second);
它会读取 second 的 pay,计算后写入 first。如果确实需要这种行为,就不能直接改成只处理当前对象;如果本来就只打算处理当前对象,无参数版本更清楚。
这段计算还需要 Overtime 类吗?
如果 Overtime 的全部职责只是乘以 1.5,当前示例可以进一步简化为:
public void calculatePayWithOvertime() {
pay = pay * 1.5;
}
这里访问的 pay 是当前对象的字段,也可以写成 this.pay。是否保留 Overtime 类,要看它是否还有其他独立职责。
这个方法会修改原来的 pay,因此连续调用两次会得到 100.0 × 1.5 × 1.5。如果业务要求每次都根据固定基础值计算,就需要保留基础值,或让方法返回计算结果而不覆盖它。
使用 this(…) 时要注意什么?
this(...)用于构造函数之间的委托,不能在普通实例方法中用它重新初始化现有对象。- 目标构造函数由传入参数匹配;如果没有匹配的重载,代码无法编译。
- 构造函数不能直接或间接循环委托,例如无参数构造函数调用带参数构造函数,后者又调用回无参数构造函数。
- 上面的示例都将
this(...)放在构造函数的第一条语句,与题目采用的写法一致。
备注:内容仅供参考。
