我们大多数人都了解共享可变性的后果,并且据说如果有机会,总是更喜欢不变性(最终修饰符,设置一次,你可以改变它)。我已经看到很少的编码示例,其中People实际上将字段final或setter设置为private或甚至删除类的setter(可以是DTO,Model或Entity类)和另一个类(可以将值设置为不可变的一个构建器类) class)用于创建和设置Immutable类的字段确保没有其他类能够修改状态。这似乎是我的负担。所以我想出了这个想法(下面我举一个例子)
public class TestDataClass {
private String name;
public String getName() {
return name;
}
public void setName(Supplier<String> supplier) throws Exception {
if(Objects.isNull(name))
this.name = supplier.get();
throw new Exception("This field is immutable and already has a value "+this.name);
}
}
通过这种方式,您可以通过setter设置值,例如objectOfTestDataClass.setName(() -> {return Perform_Desired_Logic;});
要么
objectOfTestDataClass.setName(() ->"My Name");
如果它是普通的设定者。那么你也不必创建一个bulder类或使setter私有或省略setter方法
这样,一旦你将字段设置为Immutable(我不考虑反射),我也可以在实例化期间摆脱变量初始化。
我希望您的专家意见能够验证我的想法是否合法,我可以将其视为不变性吗?我错过了什么吗?在这种情况下,请纠正我。
答案 0 :(得分:13)
有几点需要注意
使用Supplier<String>
代替String
不会改善代码。只有两种情况,即只执行一次方法并且供应商的代码立即执行的错误情况和供应商未执行的错误情况,但优化错误情况的意义何在?
如果特定属性支持null
,则该方法无法处理应该固定为“null”的情况。
API签名并不表示该类应该被视为不可变的。大多数读者会认为它是可变的。因此,如果您要删除异常抛出语句,他们可能会尝试修改对象,甚至不会注意到错误,如评论中所述。
构建器创建的不可变对象肯定已经完成,并且一旦构建就真正不可变。相反,您的类允许创建者忘记将某些属性设置为固定值,从而生成实际可变对象。
由于不保证此类的实例是不可变的,因此它们也没有保证通常与不可变对象相关联的线程安全。
答案 1 :(得分:10)
您所写的内容不允许多次设置对象的字段
但它不是一种创建具有完全初始化状态的对象的方法,因为构造函数或构建器可以提供
因此,如果客户端使用不完整或损坏状态操纵对象,则无法按预期工作。
此外,提供可由客户端在编译时调用的setter,但仅在运行时知道错误(通过抛出异常)并不是友好且设计良好的API 。< BR />
对象状态不完整的问题示例。
参加Rectangle
课程
它由4个强制性信息(高度,重量,x和y坐标)组成。
这些由4个实例字段表示:
int x,int y,int width,int height
假设该类提供了一个实例方法boolean contains(Point p)
来确定是否包含Point
(x,y坐标)。
如果将方法应用于此类的值字段,则可以创建具有不完整/部分状态的Rectangle
个实例。
contains()
方法不起作用。
它应该执行不同的检查,如果Rectangle
中缺少某个字段,它甚至应该抛出异常。
状态可能已损坏的对象问题示例
如果您的对象可能被多个线程操纵,则使用您对方法字段进行值的方式可能会将对象设置为意外且不一致的状态。
两个线程可以同时操作对象并更改两个不应以这种方式更改的字段
为避免这种情况,您不得不使用显式同步机制来读取和写入字段。
如果您使用构造函数或构建器,那么当您开箱即用完整,不可修改(事实上线程)时,您就没有这些问题安全)具有明确API 的对象。