你没有错过任何东西。你做什么完全取决于你的情况。但是,请考虑一下:
在 setter 中进行参数验证是很常见的。例如,假设我有一个类,其字段可以保存 0 到 10 的值(下面的异常类型不需要“抛出”,但为了清楚起见,我将其包括在内):
public class Example {
private int value;
public Example () {
}
public final int getValue () {
return value;
}
public final void setValue (int value) throws IllegalArgumentException {
if (value < 0 || value > 10)
throw new IllegalArgumentException("Value is out of range.");
}
}
在这里,setValue() 验证“值”以确保它遵守规则。我们有一个不变量声明“一个超出范围值的示例将不存在”。现在假设我们要创建一个带值的构造函数。你可以这样做:
public class Example {
...
public Example (int value) {
this.value = value;
}
...
}
如您所见,有一个问题。语句 new Example(11) 会成功,现在存在一个违反我们规则的示例。但是,如果我们在构造函数中使用 setter,我们也可以方便地将所有参数验证添加到构造函数中:
public class Example {
...
public Example (int value) throws IllegalArgumentException {
setValue(value); // throws if out of range
}
...
}
所以这样做有很多好处。
现在,仍然存在您可能希望直接赋值的情况。一方面,也许您没有可用的 setter(尽管我认为创建私有或包私有 setter 仍然是可取的,出于上述原因,如果需要,可以支持反射/bean,并且便于在更复杂的代码中进行验证)。
另一个原因可能是您的构造函数以某种方式提前知道将分配有效值,因此不需要验证并且可以直接分配变量。不过,这通常不是跳过使用 setter 的令人信服的理由。
但是,总而言之,尽可能在任何地方使用 setter 通常是一个好主意,这通常会导致代码更清晰、更清晰,并且随着复杂性的增加更容易维护。
您看到的大多数直接设置变量的示例只是人们“懒惰” - 如果情况允许,这是完全可以接受的(也许您正在编写一个快速测试程序或应用程序并且不想实现例如,一堆二传手)。只要您牢记大局并仅在适当的时候“懒惰”,这没有任何问题。
我想根据此处的其他一些答案添加一些内容:如果您在子类中覆盖了 setter,并且您正在设置的数据破坏了基类假定的不变量,那么应该制作相关的 setter final 或基类不应该做出这些假设。 如果重写设置器破坏了基类不变量,那么手头就有更大的问题。
您会注意到上面示例中的 getter/setter 是 final 的。这是因为我们的规则是“任何示例必须具有 0 到 10 之间的值”。因此,该规则扩展到子类。如果我们没有该规则,并且 Example 可以采用任何值,那么我们就不需要 final setter 并且可以允许子类覆盖。
希望对您有所帮助。