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

Returning an immutable reference to a mutable object in Java

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

在 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 修改对象。此类行为导致的错误可能难以追踪,因此我想将 'immutable reference' 传递给 SizeInfo

到目前为止我找到的最佳解决方案是创建一个 SizeInfo 的非静态内部 class,它只包含 getters:

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() 传递给方法而不是 [=24] =] 使其按预期工作。这在客户端很容易使用,但是必须重复每个 getter 两次而导致的代码冗余绝对不是优雅的。将 getter 仅放在 SizeInfo.Export 中并将每个 sizeInfo.getBaz() 替换为 sizeInfo.export().getBaz() 的替代方案更糟糕。这就是为什么我正在寻找一种更优雅的方法。

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

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

您仍然会传递一个可变对象,但 Figure 并不知道它:它只知道它接收到的是一个 ReadOnlySizeInfo。它仍然可以转换和改变对象,但这不再是错误:它将是一种邪恶的行为。

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

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