我曾经在一些设计书籍中读到,不可变类可以提高可伸缩性,并且在任何可能的情况下编写不可变类是良好的实践。但是我认为,不可变类会增加对象数量。那么是编写不可变类好还是使用静态类(所有方法都为静态的类)来提高可伸缩性更好呢?
我曾经在一些设计书籍中读到,不可变类可以提高可伸缩性,并且在任何可能的情况下编写不可变类是良好的实践。但是我认为,不可变类会增加对象数量。那么是编写不可变类好还是使用静态类(所有方法都为静态的类)来提高可伸缩性更好呢?
java.util.Date
等可变对象来说是一个巨大的问题。它是可变的,因此无法直接从方法中返回。这意味着您最终会做出各种防御性拷贝。这会增加对象数量。不可变类确实会促进对象的增多,但如果您想要安全性,可变对象将会促进更多的对象增多,因为您必须返回副本而不是原始对象,以防止用户更改您返回的对象。
至于使用所有静态方法的类,在大多数可以使用不可变性的情况下,这并不是一个真正的选择。以RPG为例:
public class Weapon
{
final private int attackBonus;
final private int accuracyBonus;
final private int range;
public Weapon(int attackBonus, int accuracyBonus, int range)
{
this.attackBonus = attackBonus;
this.accuracyBonus = accuracyBonus;
this.range = range;
}
public int getAttackBonus() { return this.attackBonus; }
public int getAccuracyBonus() { return this.accuracyBonus; }
public int getRange() { return this.range; }
}
如果只有包含静态方法的类,那么你要如何实现它呢?
正如所说,不可变类简化了在同步方法中的类设计和处理。
它们还简化了集合的处理,即使在单线程应用程序中也是如此。不可变类永远不会改变,因此键和哈希码不会改变,因此您不会破坏您的集合。
但是,您应该牢记您正在建模的事物的生命周期和构造函数的“重量”。如果您需要更改该事物,则不可变对象变得更加复杂。您必须替换它们而不是修改它们。这并不可怕,但值得考虑。如果构造函数需要较长时间,那么这也是一个因素。
需要考虑的一件事情是:如果你打算将一个类的实例用作HashMap中的键,或者将它们放入HashSet中,最好使它们不可变。
HashMap和HashSet依赖于对象的哈希码在对象在映射或集合中时保持不变。如果你将一个对象用作HashMap中的键,或者将其放入HashSet中,然后更改对象的状态以使hashCode()返回不同的值,则会混淆HashMap或HashSet,你会得到奇怪的结果;例如,当你迭代映射或集合时,对象存在,但当你尝试获取它时,就好像它不存在一样。
这是由于HashMap和HashSet内部的工作方式 - 它们通过哈希码来组织对象。
Java并发专家Brian Goetz的这篇文章概述了不可变对象的优缺点。
我认为,如果你想在不同的变量之间共享同一个对象,那么这个对象需要是不可变的。
例如:
String A = "abc";
String B = "abc";
A = A + "123";
System.out.println(B);
它应该输出:
abc
不变性通常用于实现可扩展性,因为在Java并发编程中,不变性是其中一个实现方式。因此,正如您所指出的那样,“不可变”解决方案中可能会有更多的对象,但这可能是提高并发性能的必要步骤。
同样重要的是,不变性的另一个用途是消耗设计意图;制作不可变类的人打算让您将可变状态放在其他地方。如果您开始改变该类的实例,则可能会破坏设计的原始意图 - 谁知道后果会是什么。