不变的阶级应该是最终的吗?
它在这篇文章中说:
Making a class final because it is immutable is a good reason to do so.
我对此感到有些困惑...从线程安全性和简单性的角度来看,不可变性是一件好事,但似乎这些问题在某种程度上与可扩展性正交。那么,为什么不变性是使课程最终定下来的一个很好的理由呢?
解决方案
因为如果类是最终的,则不能扩展它并使它可变。
即使将字段定为最终字段,也仅意味着我们不能重新分配引用,也不意味着我们不能更改所引用的对象。
我认为在不可变类的设计中没有太多用处,该类也应该扩展,因此final帮助保持不变性不变。
遵循Liskov替代原则,子类可以扩展,但不能重新定义其父级的合同。如果基类是不可变的,那么很难在不破坏合同的情况下找到可以有效扩展其功能的示例。
注意,原则上可以扩展不可变的类并更改基本字段,例如如果基类包含对数组的引用,则不能将数组中的元素声明为final。显然,方法的语义也可以通过重写来更改。
我想我们可以将所有字段声明为private,并将所有方法声明为final,但是继承的用途是什么?
我认为主要是安全性。出于同样的原因,String是最终的,任何与安全相关的代码都希望视为不可变的任何事物都必须是最终的。
假设我们有一个定义为不可变的类,将其命名为MyUrlClass,但不要将其标记为final。
现在,可能有人会想编写这样的安全管理器代码;
void checkUrl(MyUrlClass testurl) throws SecurityException { if (illegalDomains.contains(testurl.getDomain())) throw new SecurityException(); }
这是他们将其放入DoRequest(MyUrlClass url)方法中的内容:
securitymanager.checkUrl(urltoconnect); Socket sckt = opensocket(urltoconnect); sendrequest(sckt); getresponse(sckt);
但是他们不能这样做,因为我们没有使MyUrlClass最终定型。他们之所以不能这样做,是因为代码可以避免安全管理器的限制,只需覆盖getDomain()以使其在首次调用时返回" www.google.com"和" www.evilhackers.org"即可。第二,并将其类的对象传递到DoRequest()中。
顺便说一句,即使它存在,我也不会反对evilhackers.org ...
在没有安全性顾虑的情况下,这全都在于避免编程错误,这当然取决于我们如何实现。子类必须遵守其父母的合同,而不变性只是合同的一部分。但是,如果假定某个类的实例是不可变的,那么将其定型是一种确保它们确实都是不可变的好方法(即,没有可变的子类实例在附近运行,可以在父类的任何地方使用该实例)类)。
我认为我们所引用的文章不应被视为"所有不可变的类都必须是最终的"的说明,特别是如果我们有积极的理由为继承设计不可变的类时。所说的是,保护不变性是最终的正当理由,在这种情况下,虚构的性能问题(这就是当时所说的真正问题)是无效的。请注意,它给出了"一个不是为继承而设计的复杂类"作为同样有效的理由。可以说,可以避免在复杂类中不考虑继承,就像在不可变类中不考虑继承一样。但是,如果我们无法解释这一点,则至少可以通过阻止它来表明这一事实。
出于性能考虑,使类不可变也是一个好主意。以Integer.valueOf为例。当我们调用此静态方法时,它不必返回新的Integer实例。它可以安全地返回先前创建的实例,因为它在上次传递给我们该实例的引用时没有进行修改(从安全原因的角度来看,这也是很好的推理)。
我同意有效Java在这些问题上采取的立场-我们应该设计可扩展性的类,或者使它们不可扩展。如果我们打算进行扩展,则可以考虑使用接口或者抽象类。
另外,我们不必将课程定为最终课程。我们可以将构造函数设为私有。