在Java中返回对可变对象的不可变引用

时间:2016-04-30 09:29:48

标签: java immutability

基本上,我在Java中有一个可变对象,它的状态是由某个类的几个方法访问和修改的。存储在对象中的数据也被另一个类使用,但区别在于不应该允许第二个类修改数据,只是为了使用它。

在C ++中,我通常只会将const引用传递给手头的对象,但我意识到Java中没有直接的等价物。

为了澄清,以下代码段显示了我的想法:

public interface Figure
{
    void onSizeChanged(SizeInfo info);
    void draw();
}
public class FigureView extends View
{
    private Figure figure;
    private SizeInfo sizeInfo;

    ...

    public void onResize()
    {
        //Modify the state of sizeInfo, then pass it to the figure.
        figure.onSizeChanged(sizeInfo);
    }
}

让我们说SizeInfo是一个大型对象,因此我不想制作成员副本,而只是通过引用传递它。上面的代码成功地允许任何Figure访问SizeInfo对象的数据,但它也允许Figure修改对象。由此类行为引起的错误可能难以跟踪,因此我想传递一个不可变的引用'到SizeInfo

我到目前为止找到的最佳解决方案是创建一个仅由getter组成的SizeInfo非静态内部类:

public class SizeInfo
{
    //These represent the large set of data inside the class.
    private long foo;
    private double bar;
    private Export mExport = new Export();

    //Setters (only available if you have a direct reference to SizeInfo):
    public void incrementFoo()
    {
        foo++;
    }
    public void setBar(double bar)
    {
        this.bar = bar;
    }

    //SizeInfo getters:
    public long getFoo()
    {
        return foo;
    }
    public double getBar()
    {
        return bar;
    }
    public double getBaz()
    {
        return bar * foo;
    }

    //A non-static inner class:
    public class Export
    {
        public long getFoo() { return foo; }
        public double getBar() { return bar; }
        public double getBaz() { return bar * foo; }
    }
    public Export export() { return mExport; }
}

使用此代码,您只需将Figure中的方法签名从onSizeChanged(SizeInfo)更改为onSizeChanged(SizeInfo.Export),并将sizeInfo.export()传递给方法,而不是sizeInfo使其按预期工作。这在客户端非常容易使用,但由于必须重复两次getter而导致的代码冗余绝对不是很优雅。将getter仅放在SizeInfo.Export中并将每个sizeInfo.getBaz()替换为sizeInfo.export().getBaz()的替代方法更糟糕。这就是为什么我在寻找更优雅的方法。

我意识到这个特定的例子在SizeInfo太大而不能创建成员克隆方面可能不可信。但是,还有无数其他例子。例如,如果我有一个可变对象表示图像的ARGB数据(可能是由于图像是使用一些数学公式逐个像素生成的),然后想将它传递给一个不应该能够的方法要修改它,问题仍会出现。

2 个答案:

答案 0 :(得分:0)

创建一个包含getter的ReadOnlySizeInfo接口,使SizeInfo实现该接口,并将接口而不是SizeInfo传递给onSizeChanged()

你仍然会传递一个可变对象,但是图形并不知道它:所有它知道它接收到的是它是ReadOnlySizeInfo。它仍然可以投射和改变对象,但这不再是一个错误:它将是一种邪恶的行为。

答案 1 :(得分:0)

你可以创建一个界面,比如SizeInfoView,它只包含getter。然后SizeInfo将实现该接口,但也添加setter。 Figure只会收到对SizeInfoView界面的引用。调用者当然可以向下转换为SizeInfo,但您在使用const_cast的C ++中遇到同样的问题。它通常足以防止事故发生。

但请记住,您正在获取不可修改的对象,而不是不可变的对象。不同之处在于其他人可以修改它,并且更改将反映在不可修改的视图中。但同样,对于C ++ const引用也是如此。