Java 为什么这个类是可变的?

Java 为什么这个类是可变的?,java,immutability,Java,Immutability,测试类别包括: 只有一个实例变量是私有的和最终的 没有二传手 初始化实例变量的唯一方法是通过构造函数 一旦URL被设置,即使该方法被Test的任何子类覆盖,也不能在getUrl中修改它 但是我正在读的一本书说上面的测试类是可变的,因为: 这两个类都不是最终类,因此可以对其进行扩展,并且子类可以重写实例方法。但是测试类除了构造函数之外,实际上没有任何实例方法 构造函数也不是私有的 您能帮助我理解为什么测试类是可变的吗?测试的任意实例不能保证是不变的,尽管测试的直接实例是可变的。但是考虑这个子类

测试类别包括:

  • 只有一个实例变量是私有的和最终的
  • 没有二传手
  • 初始化实例变量的唯一方法是通过构造函数
  • 一旦URL被设置,即使该方法被Test的任何子类覆盖,也不能在getUrl中修改它
  • 但是我正在读的一本书说上面的测试类是可变的,因为:

    • 这两个类都不是最终类,因此可以对其进行扩展,并且子类可以重写实例方法。但是测试类除了构造函数之外,实际上没有任何实例方法

    • 构造函数也不是私有的


    您能帮助我理解为什么测试类是可变的吗?

    测试的任意实例不能保证是不变的,尽管测试的直接实例是可变的。但是考虑这个子类:

    public class Test {
        private final String url;
        public Test(String url) {
            this.url = url;
        }
        public String getUrl() {
            return url;
        }
    }
    
    然后你可以这样写:

    public class MutableTest extends Test {
            private int mutable;
            public MutableTest(String url) {
                    super(url);
            }
    
            @Override
            public String getUrl() {
                    return super.getUrl() + mutable++;
            }
    }
    

    接收已知类型为
    Test
    的对象的对象将知道该对象是不可变的。然而,接收类型为
    Test
    的非空引用的对象将没有语言定义的方式来确保由此标识的对象不可变。有些人似乎认为这是一个主要的担忧。不过,总的来说,我认为不应该这样


    如果一个类是有效的可继承的,那么设计一个完全不适合任何非人为目的的派生类型通常是很容易的。语言甚至可以尝试阻止这种情况的唯一方法是极大地限制派生类型可以做的有用事情的范围。但是,除了一些特殊类型的类(通常是与安全性相关的类)之外,一般来说,最好确保派生类可以做有用的事情,而不是担心它们做人为的和无用的事情的可能性。

    那是什么书?使方法
    getUrl
    final,那么
    Test
    的对象将是不可变的。@adityanarayandxit不是真的,你仍然可以在子类中添加可变状态。同意,但是,对象的状态(测试对象中url的值)仍然没有修改。只是调用者现在得到了不同/错误的值。@Ram该字段
    mutable
    被添加为状态的一部分。因此,每次调用getter时,状态都会发生变化。“但是数据本身是不可修改的”——这取决于您的观点。对于客户端,字段是
    private
    ,因此该类唯一的“数据”是您可以通过
    getUrl
    获得的数据。事实上,这是可以修改的,甚至只需调用函数。想象一下,即使
    getUrl
    返回相同的字符串,但仍然会增加计数器。。。
    Test
    的任何使用者都不能确定(没有难看的技巧,比如检查运行时类型)调用
    getUrl
    有时不会生成溢出异常,但其他情况则不会,这取决于实例所在的位置。@CompuChip抓住了本质。我有点同意这是吹毛求疵,但无论如何,一个任意的
    Test
    实例不能保证是不可变的(因为它也可以是一个可变的子类),尽管
    Test
    中的所有字段都是最终的和不可变的。
    Test
    呈现给客户端的接口肯定不是不可变的。是的,但不可变并不意味着它为每个实例方法调用返回相同的值。它提供的值可以更改,但并不意味着其状态已更改。您可以拥有一个不可变的
    RandomNumberGenerator
    (例如,您不能修改种子),但每次请求时都会返回一个新的随机数。
    Test instance = new MutableTest("http://example.com/");
    String firstGet = instance.getUrl();
    String secondGet = instance.getUrl();
    assertEquals(firstGet, secondGet); // Boom!