我正在查看有关SO的其他问题,但我没有真正看到我的问题的解释。我读到从另一个构造函数调用构造函数(使用this关键字)是有效,但我不明白为什么它是有效的。
以前,我认为每个对象只能运行一个构造函数。构造函数链接似乎打破了这个逻辑,因为在调用一个构造函数时,它会与原始的目标构造函数一起运行另一个构造函数。为什么构造函数链接工作?
答案 0 :(得分:14)
我们在同一个类中从其他链接(调用)一个构造函数,以便我们可以避免代码重复。如果不对每个构造函数进行链接,我们最终会重复业务细节,导致代码重复,并且难以维护代码。
想象一下,你正在创建一个总线。
public class Bus {
int noOfSeats;
String busColor;
public Bus() {
this(40); //// Using another constructor and proceeding with default values..
}
public Bus(int seats) {
this(seats,"red"); // Using another constructor and proceeding..
}
public Bus(int seats, String color) {
this.noOfSeats = seats;
this.busColor = color;
}
}
使用此类的人一次只能使用构造函数,而在内部使用链接。想象一下,你有一个方法用默认值初始化东西并在构造函数中调用它。这没什么不对,对吗?
只是为了澄清,创建对象的人只调用一个构造函数。被调用的构造函数调用该类内部的其他内容。
答案 1 :(得分:10)
委托构造函数减少了重复代码的数量,因为您可以通过从另一个构造函数中调用它来利用其中设置的功能。
在将此功能设计为语言之前,我们必须依赖于从构造函数调用的“初始化”类型函数。这是一个痛苦有两个原因(i)你永远不能保证函数只是从构造函数调用,而(ii)从构造函数调用类方法总是一个设计“气味”。
答案 2 :(得分:4)
它允许澄清逻辑并减少代码。
Is nesting constructors (or factory methods) good, or should each do all init work
将构造函数链接在一起是合理的,具有较少参数的版本调用具有更多参数的版本。它非常清楚发生了什么,所有真正的“逻辑”(超出默认值)都在一个地方。例如:
public class Foo {
private static final int DEFAULT_X =10;
private static final int DEFAULT_Y =20;
private int x;
private int y;
private int precomputedValue;
public Foo(int x, int y) {
this.x = x;
this.y = y;
precomputedValue = x * y;
}
public Foo(int x) {
this(x, DEFAULT_Y);
}
public Foo() {
this(DEFAULT_X, DEFAULT_Y)
}
}
答案 3 :(得分:2)
这里不是Java开发人员,但在Python中,这也是允许的,而且非常正常。
每当你继承一个类时,你可能想在添加你自己的东西和食谱之前调用父类的构造函数。
这使您可以受益于父类的构造功能。每当你不确切知道父类的内容时,你总是总是首先调用它,以避免父类的特性和方法被破坏。
答案 4 :(得分:2)
混淆源自每个实例应该调用一个ctor 是绝对正确的。链接另一个的构造函数用于避免代码重复和简单,因为复制相同的代码实际上是开销。有时我们可能需要两个构造函数,第一个只需要两个参数,另一个需要三个参数。但是,三个参数-ctor使用两个参数-ctor的相同变量。您是希望复制粘贴相同的分配还是仅通过this
调用另一个ctor?
答案 5 :(得分:1)
作为其他答案的补充,我想分享另一种用于从我经常使用的构造函数中调用构造函数的用法。
我觉得有用的另一种构造函数链接策略是为接口提供默认实现/自动注入。
public class Foo {
private readonly ILogger _logger;
//Default implementation for most cases
public Foo() : this(new DefaultLoggerImplementation()) {}
//DI Constructor that allows for different implementations of ILogger
//(Or Mocks in testing)
public Foo(ILogger logger)
{
_logger = logger;
}
public void Log(string message)
{
_logger.log(message);
}
}
这样,如果你有一个接口的默认实现,它将通过new Foo()
如果您的案例具有不同的ILogger
行为,则它很容易注入,这也提升了在单元测试框架中模拟界面的能力。