是否有可变 public 字段的用例?

Are there use-cases for mutable public fields?

我想知道是否存在 public 字段合理的用例(对于可变值),或者应该不惜一切代价避免使用 getters/setters 应该始终使用。

假设有一个 DTO(数据传输对象)/Java Bean,它没有任何逻辑,只是保存数据(添加更多这可能是协议的一部分,它不会改变,只会增强) . 实际上不存在要封装的实现细节,因为没有实现,只有数据。 还假设 Bean Validation 框架用于通过注释验证对象,因此没有用于验证逻辑的 setter 的目的。

为什么 setter 和 getter 会打扰我?

没有setters/getters更容易阅读;字段,尤其是内部 类 (您不必滚动到 setter 和 getter 即可到达其他字段)。此外,user.passworduser.getPassword() 更清楚,如 Groovy.

我知道龙目岛项目;但我已经有很多来自 Bean Validation 的注释。

所以要弄清楚这个问题:上述情况是否适用于 public 个字段,还是 setters/getters 更好? 为什么?

might the above scenario be justified for public fields or is it still better to have setters/getters?

情况似乎是您对滚动 getter 和 setter 感到困扰。这在我的书中不是一个很好的理由,但如果你可以保持这些 类 包私有或私有嵌套 类,那么 Joshua Bloch 没问题,因为未来可能发生变化的范围。

有效 Java 项目 14: 在 public 类 中,使用访问器方法,而不是 public 字段

If a public class exposes its data fields, all hope of changing its representation is lost, as client code can be distributed far and wide.

However, if a class is package-private or is a private nested class, there is nothing inherently wrong with exposing its data fields - assuming they do an adequate job of describing the abstraction provided by the class.

就我个人而言,我比较严格,我遵循的经验法则是字段在 编译单元 之外不得可见。例如:

public class Outer {
    private int fieldMustBePrivate;

    public class PublicNested {
        private int fieldMustBePrivate;
    }

    private class PrivateNested {
        int fieldOkToBePackagePrivate;
    }

    public void exampleUsage() {
        PrivateNested privateNested = new PrivateNested();
        privateNested.fieldOkToBePackagePrivate = 123;
    }
}

一个常见的情况是,有时在实施或执行的中间,您发现您的记录以空用户名结尾。

现在,您可以:

  • 搜索所有被属性赋值的地方,查找被赋值为null的地方。它可能是 20 个位置,而且很可能不会是 user.userName = null,而是 String userName = null; ..... user.userName = userName

  • 向您的 setUserName() 方法添加一个检查,该方法在尝试设置空值时启动异常。您获得了完整的堆栈跟踪,并且程序的运行在 将错误数据引入您的持久层之前停止。

因此,直接访问属性的用例是当您确定您的代码中绝对没有(并且将会)没有错误时。

我会坚持使用二传手。

此外,一些(许多)框架依赖于使用 setter 和 getter 来访问属性。

目前的观点是你应该有可变的public字段。

它来自两个不同方向的Principle of least astonishment

首先,通常的做法是将所有字段设为私有,只能通过 getter 和 setters 进行访问。有很多代码可以做到这一点,当您遇到不这样做的代码时,令人惊讶

其次,它给了你可预测性。您可以保证 没有人会在您的 setter 以外的任何地方更改字段。因此,您可以在 setter 上放置一个断点,并且 知道 对该字段的每次更改都会到达断点。

但是,有一些纯数据情况,就像您描述的那样,很容易在其中创建字段 public,甚至更罕见,不是最终的。

我的工具箱中有一个 class Pair,其中有 public 个字段:

public class Pair<P, Q> {

    // Exposing p & q directly for simplicity. They are final so this is safe.

    public final P p;
    public final Q q;

    public Pair(P p, Q q) {
        this.p = p;
        this.q = q;
    }

是的,有:

  • 性能
  • 代码简洁

调用一个函数(accessors/mutators 是函数)可能看起来没什么大不了的,但是当翻译成汇编代码时,这意味着你必须将函数参数压入堆栈,压入 return地址入栈,执行一个CALL函数,执行函数体,然后将结果压入栈中,弹出原始参数,弹出return地址并转到原始地址......这取决于略有不同关于调用约定,我可能以错误的顺序列举了步骤,但通常调用函数时确实会产生开销。

因此,对于像 getter/setter 这样可能只包含一两个汇编指令的愚蠢函数,开销与有效负载的数量级相同,甚至可能更大。如果您正在开发游戏或任何其他对性能要求很高的软件,摆脱函数调用是优化技术之一。

关于第二个用例,代码简洁,getter 和 setter 什么都不做(他们不做验证,不同步,不复制,什么都不做)实际上是多余的,我们只是代码他们,因为这是 Java 中的惯例。但它们毫无用处。其他人(例如:Google)不要盲目地遵循这个约定。

较新的语言通常会提供一些机制来自动创建这些方法,甚至完全跳过它们,依靠成员访问修饰符来提供封装。例如,C#'s properties 提供自动 getter/setter,但允许您的代码调用它们,就好像它们是 public 成员一样。