在我们所有的项目中,我们使用的数据类根据定义包含数据(字段),但不包含(业务)逻辑。
根据最佳编码实践,数据类最好应该是不可变的,因为不可变性意味着线程安全。 这里的主要参考是Joshua Bloch的Effective Java书籍; Yegor Bugayenko的帖子也很有趣。
不可变类具有几个有趣的属性:
- 它不能是子类的(即它应该是最终的,或者应该具有静态工厂方法和私有构造函数)
- 所有字段均应为私有字段(以防止直接访问)
- 所有字段应写入一次(在实例创建时)(即,它们应该是最终值且没有设置方法)
- 所有可变类型(例如java.util.Date)字段都应受到保护,以防止客户端通过引用进行写访问
不变类的示例如下:
public final class ImmutableBean {
private final String aStr;
private final int anInt;
public ImmutableBean(String aStr, int anInt) {
this.aStr = aStr;
this.anInt = anInt;
}
public String getAStr() {
return aStr;
}
public int getAnInt() {
return anInt;
}
}
注意:在Java中很常见,有很多样板代码隐藏了不变性定义。
像Project Lombok这样的库使我们的生活更轻松,因为我们可以使用@Value批注轻松地定义一个不可变的类,如下所示:
@Value
public class LombokImmutableBean {
String aStr;
int anInt;
}
更具可读性。
我们(单元)应该测试一个类以检查其不变性吗?
在理想世界中,答案是否定的。
借助我们首选的IDE自动代码生成功能或Lombok之类的库,为类添加不变性并不难。
但是在现实世界中,当我们创建类或稍后(或可能是团队的初级成员)修改类时,可能会发生人为错误。 如果不使用final来添加新字段并且使用IDE代码生成器生成了setter会怎样? 该类不再是不变的。
重要的是要确保该类在整个项目生命周期中都是并且保持不变。
借助Mutability Detector,我们可以轻松创建一个测试来检查类的不变性状态。
像往常一样,可以在Maven Central上找到Maven / Gradle依赖项。
为了测试我们的ImmutableBean,我们可以创建以下jUnit测试类:
import static org.mutabilitydetector.unittesting.MutabilityAssert.assertImmutable;
public class ImmutableBeanTest {
@Test
public void testClassIsImmutable() {
assertImmutable(ImmutableBean.class);
}
}
如果类不是不可变的,则测试将失败。
例如,如果一个字段不是final且具有setter方法,则测试将失败,并且错误消息的描述性非常强:
org.mutabilitydetector.unittesting.MutabilityAssertionError:
Expected: it.gualtierotesta.testsolutions.general.beans.ImmutableBean to be IMMUTABLE
but: it.gualtierotesta.testsolutions.general.beans.ImmutableBean is actually NOT_IMMUTABLE
Reasons:
Field is not final, if shared across threads the Java Memory Model will not guarantee it is initialised before it is read.
[Field: aStr, Class: it.gualtierotesta.testsolutions.general.beans.ImmutableBean]
Field [aStr] can be reassigned within method [setaStr]
[Field: aStr, Class: it.gualtierotesta.testsolutions.general.beans.ImmutableBean]
完整的项目可以在我在GitHub上的Test Solutions画廊项目中找到。 参见模块常规 。
我建议的方法是使用Lombok,而不进行任何不变性测试。 如果不能使用Lombok(例如在旧项目中),请使用“可变性检测器”声明该类确实是不可变的。