当前位置: 首页 > 知识库问答 >
问题:

不可变对象的所有属性必须是最终的吗?

曹育
2023-03-14

不可变对象的所有属性都必须是最终的吗?

我会说他们没有。但是我不知道我是对是错。

共有3个答案

孟鹤龄
2023-03-14

不可变=不可变。因此,将属性设为最终值是一个好主意。如果一个对象的所有属性都没有被保护,我不会说这个对象是不可变的。

但是如果一个对象没有为它的私有属性提供任何设置器,那么它也是不可变的。

韩弘阔
2023-03-14

您可以通过单独封装轻松地保证不变性,因此不需要:

// This is trivially immutable.
public class Foo {
    private String bar;
    public Foo(String bar) {
        this.bar = bar;
    }
    public String getBar() {
        return bar;
    }
}

然而,在某些情况下,您还必须通过封装来保证这一点,因此这是不够的:

public class Womble {
    private final List<String> cabbages;
    public Womble(List<String> cabbages) {
        this.cabbages = cabbages;
    }
    public List<String> getCabbages() {
        return cabbages;
    }
}
// ...
Womble w = new Womble(...);
// This might count as mutation in your design. (Or it might not.)
w.getCabbages().add("cabbage"); 

这样做是为了捕捉一些微不足道的错误,并清楚地展示您的意图,这不是一个坏主意,但是“所有字段都是最终的”和“类是不可变的”不是等价的陈述。

熊锐进
2023-03-14

不可变对象(所有属性都是最终的)和有效不可变对象之间的主要区别是安全发布。

您可以在多线程上下文中安全地发布不可变对象,而不必担心添加同步,这要感谢Java内存模型为最终字段提供的保证:

最终字段还允许程序员在没有同步的情况下实现线程安全的不可变对象。线程安全的不可变对象被所有线程视为不可变的,即使使用数据竞争在线程之间传递对不可变对象的引用。这可以提供安全保证,防止不正确或恶意代码滥用不可变类。必须正确使用最终字段来提供不变性的保证。

顺便说一句,它还可以强制执行不变性(如果您尝试在类的未来版本中改变这些字段,因为您忘记了它应该是不可变的,它将无法编译)。

澄清

>

  • 将对象的所有字段设为最终字段并不会使其不可变-您还需要确保(i)其状态不能更改(例如,如果对象包含最终列表,则在构造后不必须执行变异操作(添加、删除…)和(ii)您不能让this在构造期间逃逸
  • 一个有效的不可变对象在安全发布后是线程安全的
  • 不安全发布示例:

    class EffectivelyImmutable {
        static EffectivelyImmutable unsafe;
        private int i;
        public EffectivelyImmutable (int i) { this.i = i; }
        public int get() { return i; }
    }
    
    // in some thread
    EffectivelyImmutable.unsafe = new EffectivelyImmutable(1);
    
    //in some other thread
    if (EffectivelyImmutable.unsafe != null
        && EffectivelyImmutable.unsafe.get() != 1)
        System.out.println("What???");
    

    这个程序理论上可以打印what??。如果i是最终的,那将不是法律结果。

  •  类似资料:
    • 问题内容: 不可变对象是否必须具有所有属性final? 据我说不是。但是我不知道我是否正确。 问题答案: 不可变对象(所有属性都是final)和有效不可变对象(属性不是final,但不能更改)之间的主要区别是安全发布。 由于Java内存模型为最终字段提供了保证,因此您可以在多线程上下文中安全地发布不可变对象,而不必担心添加同步: final字段还允许程序员无需同步即可实现线程安全的不可变对象。即使

    • 本文向大家介绍JavaScript变量Dom对象的所有属性,包括了JavaScript变量Dom对象的所有属性的使用技巧和注意事项,需要的朋友参考一下 DOM对象的HTML:   <button>Disable State</buttom> 1,遍历DOM对象所具有的属性(全部,不管是否在HTML tag中是否设置) 结果: 2.遍历DOM对象在HTML中设置过的属性:   每一个attribut

    • 问题内容: 这个问题已经在这里有了答案 : 为什么在匿名类中只能访问最终变量? (15个答案) 为什么实例变量“忽略Lambda表达式中使用的变量必须是最终变量或实际上是最终变量”警告[重复] (2个答案) Lambdas:局部变量不需要最终变量,实例变量不需要 (10个答案) 2年前关闭。 当我编写此代码时,我收到一个编译时错误,该错误是: “ lambda中的变量必须是final或有效的fin

    • 这肯定是个愚蠢的问题,但我对Kotlin真的是个新手,我没有找到任何解决办法。 如何声明类字段?就像我们可以在Java中拥有它一样: 在中: 但我得到一个警告:“属性必须初始化或抽象”

    • 如果属性值可以设置为 null,则其被认为是可选的(可空的)。相反,如果属性值设置为 null 是非法的,那么它就被认为是必须的属性。 惯例 按照惯例,值可以是 null 的运行时类型(string、int?、byte[] 等等)属性将被配置为可选。值不能为 null 的运行时类型(int、decimal、bool 等等)属性则将被配置为必须。 注意 值不能为 null 的运行时类型属性是无法被配

    • 问题内容: 例如,如果我有一个变量,并且在主线程中声明了一个runnable,并且想将x传递给该runnable的方法,则必须对其进行声明。为什么? 问题答案: 因为如果能够更改它们,可能会导致很多问题,请考虑以下事项: 这是一个粗略的示例,但您可以看到可能发生许多无法解释的错误。这就是变量必须为最终变量的原因。这是解决上述问题的简单方法: 如果您需要更完整的说明,则有点像同步。Java希望防止您