为什么这个类可变?

Ram*_*Ram 41 java immutability

public class Test {
    private final String url;
    public Test(String url) {
        this.url = url;
    }
    public String getUrl() {
        return url;
    }
}
Run Code Online (Sandbox Code Playgroud)

Test类有:

  1. 只有一个私有和最终的实例变量.
  2. 没有二传手.
  3. 初始化实例变量的唯一方法是通过构造函数.
  4. 一旦设置了URL,即使在getUrl中也不能修改它,即使该方法被Test的任何子类覆盖.

但是我正在阅读的一本书说上面的Test类是可变的,因为:

  • 这两个类都不是最终的,因此它可以扩展,并且子类可以覆盖实例方法.但Test类实际上没有构造函数以外的任何实例方法.

  • 构造函数也不是私有的.

你能帮我理解为什么Test类是可变的吗?

gus*_*afc 58

Test尽管是直接实例,但不保证任意实例是不可变的Test.但请考虑这个子类:

public class MutableTest extends Test {
        private int mutable;
        public MutableTest(String url) {
                super(url);
        }

        @Override
        public String getUrl() {
                return super.getUrl() + mutable++;
        }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以这样写:

Test instance = new MutableTest("http://example.com/");
String firstGet = instance.getUrl();
String secondGet = instance.getUrl();
assertEquals(firstGet, secondGet); // Boom!
Run Code Online (Sandbox Code Playgroud)

  • @Ram字段`mutable`作为状态的一部分添加.因此,每次调用getter时都会更改状态. (8认同)
  • "但这样的数据虽然不可修改" - 这取决于你采取的观点.对于客户端,该字段是"private",因此该类具有的唯一"数据"是您可以通过`getUrl`获得的.这是可以修改的,实际上甚至只需调用该函数即可.想象一下,即使`getUrl`返回相同的字符串,但仍会增加计数器......没有`Test`的消费者可以确定(没有丑陋的技巧,比如检查运行时类型)调用`getUrl`不会产生溢出有时是异常,但不是其他异常,具体取决于您的实例所在的位置. (6认同)
  • @CompuChip捕捉到了本质.我可以认为这是分裂的头发,但是,一个任意的`Test`实例不能保证是不可变的(因为它也可以是一个可变的子类),尽管`Test`中的所有字段都是最终的,不可改变的.并且`Test`给客户提供的_interface_绝对不是一成不变的. (3认同)
  • 是的但是不可变并不意味着它为每个实例方法调用返回相同的值.它提供的价值可以改变,但并不意味着它的状态已经改变.您可以拥有一个不可变的`RandomNumberGenerator`(例如,您无法修改种子),但每次请求一个随机数时都会返回一个新的随机数. (2认同)