不可变类型是否需要复制构造函数?

时间:2017-01-17 08:40:53

标签: java immutability

说我有一个不可变的DecimalNumber类:

public final class DecimalNumber {

    public final String str;

    public DecimalNumber(String str) { this.str = str; }
    public DecimalNumber(DecimalNumber copy) { this(copy.str); }

    public boolean isZero() {...}

    public DecimalNumber add(DecimalNumber other) {...}

    ...

}

我决定像这样实施add

public DecimalNumber add(DecimalNumber other) {

    if (other.isZero())
        return /* the same object */

    ...

}

我应该返回this(内存使用量减少)还是复制对象new DecimalNumber(this)

我认为简单地返回this应该没问题,但是创建新对象是有益还是有理由?或者它是首选的?

5 个答案:

答案 0 :(得分:2)

如果一个类是不可变的并且是final,那么你可以返回this

如果它不是最终的,则无法确定this实例是否真的不可变。您实际上可能正在处理一个增加可变状态的子类。

请注意,在以下情况下,类只是真正不可变的:

  • 所有领域都是最终的,即使是私人领域。 (由于Java内存模型允许其他线程查看非最终字段的未完成/默认值,因此仅使用getter的非final字段是不够的。)
  • 其所有字段都是不可变类本身,或者对它们的访问受到限制,以便您可以确定它们永远不会被更改。

在您的情况下,这两个条件得到满足,因为String是一个不可变类。如果你知道你的类没有子类存在,那么你可以(事实上,应该恕我直言)返回this。为了确保您的类的子类不存在,您可以将其设为final

答案 1 :(得分:1)

为什么回归this感到奇怪?

如果你要返回一个新对象,那么首先不需要if,所以返回new DecimalNumber(this)是没有选择的!

答案 2 :(得分:1)

由于您的对象是不可变的,我们需要在更改时创建另一个副本。但是在这里,添加零不会改变对象的值。 因此,我们可以返回相同的对象。

考虑String.java中的concat()代码以供参考:

public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        char buf[] = new char[count + otherLen];
        getChars(0, count, buf, 0);
        str.getChars(0, otherLen, buf, count);
        return new String(0, count + otherLen, buf);
    }

如您所见,返回同一个对象没有任何害处。

答案 3 :(得分:1)

无需复制不可变值对象。由于它是不可变的,因此原件和副本在所有时间都是等效的,因此有两个副本是没有意义的。

我的示例不仅仅是dataRequest = DataClass() ,我还会更进一步,完全删除你的复制构造函数。

例如,Java return this类实际上有一个复制构造函数。但所有这一切都是由缺乏经验的开发人员编写的代码,他们要么没有意识到它是不可改变的(或者不理解这意味着什么),所以他们会String或{{0}} {1}},它只会浪费内存和处理器周期。

答案 4 :(得分:0)

如果一个对象真的是不可变的,我会返回它。你根本不需要两个肯定总是相等的实例。

https://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html