基本上,我在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数据(可能是由于图像是使用一些数学公式逐个像素生成的),然后想将它传递给一个不应该能够的方法要修改它,问题仍会出现。
答案 0 :(得分:0)
创建一个包含getter的ReadOnlySizeInfo
接口,使SizeInfo实现该接口,并将接口而不是SizeInfo传递给onSizeChanged()
。
你仍然会传递一个可变对象,但是图形并不知道它:所有它知道它接收到的是它是ReadOnlySizeInfo
。它仍然可以投射和改变对象,但这不再是一个错误:它将是一种邪恶的行为。
答案 1 :(得分:0)
你可以创建一个界面,比如SizeInfoView
,它只包含getter。然后SizeInfo
将实现该接口,但也添加setter。 Figure
只会收到对SizeInfoView
界面的引用。调用者当然可以向下转换为SizeInfo
,但您在使用const_cast
的C ++中遇到同样的问题。它通常足以防止事故发生。
但请记住,您正在获取不可修改的对象,而不是不可变的对象。不同之处在于其他人可以修改它,并且更改将反映在不可修改的视图中。但同样,对于C ++ const
引用也是如此。