返回对 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
引用也是如此。
基本上,我在 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
引用也是如此。