java - 为什么我要在没有设置器的情况下创建不可变类时将字段声明为最终字段
问题描述
我目前在这里阅读,为了实现我的对象的不变性,我必须:
- 将我的所有字段声明为私有和最终字段
- 不定义二传手
如果无论如何都没有设置器,为什么我需要将字段声明为 final。java编译器不允许这样的事情:
myObj.getSomething() = new Somthing()
如果我尝试使用反射,那么final
关键字不会阻止我更改引用。
我在这里找到了一个很好的解释,为什么整个类都需要,final
但没有关于为什么私有字段需要是最终的。
编辑:作为对 GotoFinal评论的回复, 这是一个展示如何使用反射编辑字段的类:
public class Test {
static class Immutable {
private final StringBuilder immutableField = new StringBuilder("You can't set final field just by normal reflections");
public StringBuilder getStringBuilder() {
return immutableField;
}
}
public static void main(String[] args) throws Exception {
Immutable immutableObject = new Immutable();
Field f1 = immutableObject.getClass().getDeclaredField("immutableField");
f1.setAccessible(true);
f1.set(immutableObject, new StringBuilder("Well, I just did"));
System.out.println(immutableObject.getStringBuilder());
}
}
解决方案
它不需要是最终的,但它是一个很好的做法,因为在阅读源代码时,您会立即知道给定字段不能更改 - 包括类内部或本地类 - 在您的类中声明(java将生成特殊的桥接器然后为那个领域)。
此外,由于字段是最终的,您需要在构造函数中对其进行初始化 - 所以在这里更难出错。
它也可能会影响反序列化器的工作方式,因为大多数库不会尝试修改最终字段。
推荐阅读
- laravel - 当我运行 php artisan token:generate 1 in laravel 时找不到类,所以我可以获得 Swagger UI Auth 的不记名令牌
- c# - 我可以使用 lambda 表达式忽略事件中的代码吗
- c - 使用 /dev/random 生成函数,调用时显示分段错误(核心转储)错误
- algorithm - 是否有来自 st 的路径通过特殊节点?
- java - PassiveExpiringMap 没有过期的对象
- jquery - 使用 Power Query 通过 REST API 查询分页 XML
- xcode - 如何在 Azure Pipelines 中升级 XCode?
- html - 'counter-reset' 属性是否总是必须在父容器上定义?
- c# - 在不相关的组件上调用 OnSetParameterAsync
- javascript - 如何在 Nodejs 中使用对象