我想知道是否存在公共字段合理的用例(对于可变值)或者应该不惜一切代价避免使用,并且应该始终使用getter / setter。
假设有一个DTO(数据传输对象)/ Java Bean,它没有任何逻辑,只是保存数据(添加更多这可能是协议的一部分,不会改变,只会增强)。 实际上没有要封装的实现细节,因为没有实现,只有数据。 还假设Bean Validation框架用于通过注释验证对象,因此没有用于验证逻辑的setter的目的。
为什么安装者和吸气者会困扰我?
没有setter / getters,它更容易阅读;字段,特别是对于内部类(您不必滚动到设置者和getter到达其他字段)。同样,在Groovy中使用user.password
而不是user.getPassword()
也更清楚。
我知道项目龙目岛;但我已经从Bean验证中获得了很多注释。
所以要明确这个问题:上述场景是否可以用于公共领域,或者是否更适合拥有setter / getter? 为什么?
答案 0 :(得分:1)
一个常见的情况是,在实施或执行过程中,您会发现您的记录以空用户名结束。
现在,您要么:
搜索所有位置,分配属性以查找为其分配空值的位置。它可能是20个地方,最有可能的是,它不是user.userName = null
而是String userName = null; ..... user.userName = userName
。
在setUserName()
方法中添加一项检查,以便在尝试设置空值时启动异常。您将获得完整的堆栈跟踪,并且在向您的持久层引入错误数据之前停止该程序的操作。
因此,直接访问属性的用例是当您确定代码中存在(并且将来)绝对没有错误时。
我会坚持使用二传手。
此外,一些(许多)框架依赖于使用setter和getter来访问属性。
答案 1 :(得分:1)
目前的观点是,你应该不拥有可变的公共字段。
它来自两个不同方向的Principle of least astonishment。
首先,通常的做法是将所有字段设为私有,只能通过getter和setter访问。有很多代码可以解决这个问题令人惊讶当你遇到没有的代码时。
其次,它为您提供了可预测性。您可以保证,除了您的setter之外,没有人会更改其他任何地方的字段。因此,您可以在设置器上设置一个断点,并知道,该字段的每次更改都将达到断点。
然而,有一些纯粹的数据情况,比如你描述了将字段公之于众的诱惑,甚至更为罕见,而不是最终的。
我的工具箱中有一个类Pair
,它有公共字段:
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;
}
答案 2 :(得分:0)
上述情况可能适用于公共领域,还是有更好的设置者/吸气剂?
场景似乎是你在滚动浏览getter和setter时感到困扰。这不是我书中的一个很好的理由,但是如果你可以保留这些类包私有或私有嵌套类,那么Joshua Bloch就可以了,因为未来潜在的变化范围。
有效Java项14:在公共类中,使用访问器方法,而不是公共字段
如果公共类暴露了它 数据字段,因为客户端代码可以分发,所以改变其表示的所有希望都会丢失 广泛的。
但是,如果一个类是包私有的或者是一个私有的嵌套类,那么就有了 暴露其数据字段本质上没有错误 - 假设他们在描述类提供的抽象方面做了足够的工作。
就个人而言,我更严格一点,我遵循的经验法则是编译单元之外的字段不可见。 e.g:
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;
}
}
答案 3 :(得分:0)
是的,有:
调用函数(访问器/增变器是函数)可能看起来不是什么大问题,但是当转换为汇编代码时,这意味着您必须将函数参数推送到堆栈,将返回地址推送到堆栈,执行CALL函数,执行函数体,然后将结果推送到堆栈,弹出原始参数,弹出返回地址并转到原始地址......这根据调用约定略有不同,我可能已经枚举了以下步骤错误的顺序,但通常在调用函数时会有开销。
因此对于像getter / setter这样的愚蠢函数可能只包含一个或两个汇编指令,开销与有效负载的数量级相同,可能更大。如果您正在开发具有高性能要求的游戏或任何其他软件,那么摆脱函数调用是优化技术之一。
关于第二个用例,代码简洁,getter和setter什么都不做(它们没有验证,没有同步,没有复制,没有任何东西)实际上是多余的,我们只是编写代码,因为它是Java中的约定。但他们没有任何目的。其他人(例如:谷歌)不会盲目地遵循这个惯例。
较新的语言通常提供一些机制来自动创建这些方法,甚至完全跳过它们,依靠成员访问修饰符来提供封装。例如,C#'s properties提供自动获取/设置器,但允许您的代码将它们称为公共成员。