问题是我无法修改我自己内部写入的对象,从而制作了一个我修改并返回的克隆。但是,在其他函数中我直接修改调用该方法的对象。我希望,并且人们已经建议,保持一致,因此用户可以使用返回的克隆执行任何他们喜欢的操作,而无需修改实际对象。
我必须在某些功能中返回对象的修改克隆,因为它没有办法解决它(我意识到)。除了标题中的问题之外,最好是采用一致的做事方式(导致我即使是最轻微的改变也会返回克隆),或者如果我有不同的方式来响应同一类中的用户,那还可以吗?
[编辑]这样做更好:
public Image fillWithColor(Color fillColor) {
Image newImage = new Image(this.getWidth(), this.getHeight(), this.getType());
for (int x = 0; x < this.getWidth(); x++) {
for (int y = 0; y < this.getHeight(); y++) {
newImage.setPixel(x, y, fillColor.getRGB());
}
}
return newImage;
}
或者这个:
public void fillWithColor(Color fillColor) {
for (int x = 0; x < this.getWidth(); x++) {
for (int y = 0; y < this.getHeight(); y++) {
this.setPixel(x, y, fillColor.getRGB());
}
}
}
答案 0 :(得分:1)
由于各种重要原因,大趋势是将尽可能多的数据视为只读。甚至数据库今天都这样做。
你显然已经意识到不受控制的数据修改会让你陷入困境。很好。尝试将数据设计为不可变对象,从长远来看,许多事情将变得更加容易。请注意,Java中的某些数据结构本质上是可变的(数组,哈希表等),并且意味着并且预计会发生变异。
在上面的例子中,我选择了第一个变体。为什么?复制图像可能需要几微秒和一些RAM,而不是就地更新。但是你可以保留旧的图像,根据你的应用,这可能是有益的。此外,您可以在10个不同的线程中并行使用10种不同的填充颜色对同一图像进行着色,并且不存在锁定问题。
话虽如此,仍然无法像#34那样回答你的问题;它总是更好......&#34;。这取决于您的问题,您的环境,编程语言,您正在使用的库以及更多因素。
所以,让我们说,不可变数据在大多数情况下都是可取的,除非有严重的理由反对它。 (在执行时间上节省几微秒通常不是一个严重的原因。)
换句话说,应该有充分的理由使数据类型可变,而不变性应该是默认值。不幸的是,Java不是支持这种方法的语言,相反,默认情况下一切都是可变的,并且需要花费一些力气才能使它与众不同。
答案 1 :(得分:1)
唯一正确的答案是:
“取决于”。
这就是工程学的全部意义所在。做出正确的交易。假设您是一名在该市工作的工程师,并负责为市中心设计新的垃圾箱。你有一些决定要做。你想让它们变大,所以它们可以包含大量垃圾,并且在繁忙的日子里不会溢出。但是你也想让它们变小,这样它们就不会占用人行道上的大量空间。你想让它们变亮,这样它们在排空时可以很容易地处理,而且很重,所以它们不会被风吹过或被流氓踢过。因此,这不是一个大或小,重或轻的问题,而是多大和多重的问题。
在软件工程中,还有许多相互排斥的品质,您必须在项目中做出正确的选择。一些例子:
不可变与可变
不可变类型的优点是它们是线程安全的。线程A和B都可以引用同一个对象,但仍然可以确保它的值不会在不使用锁的情况下意外更改。如果线程A想要更改值,那么它可以通过更改它保存到新对象的引用来实现;线程B仍然很高兴地抓住原始对象。
让对象的值意外更改不仅是并发编程中的问题,而且当您的类的用户不期望它时也会发生。这就是defensive copying概念的原因。
java中的stock Date
类是可变的。因此,请考虑使用Person
getter和getBirthDate()
setter的setBirthDate()
类。作为Person
课程的用户,您只能通过使用设定者来更改此人的出生日期,但如果您的获取者未返回防御性副本,则该课程的用户也可以更改意外地通过更改从Date
收到的getBirthDate()
对象。
因此,不可变类型使程序成为线程安全(r)且不易出错,因此通常是一个好主意,但您不能总是使用它们。您的fillWithColor()
函数是一个不太可行的示例。
Canvas
类是一个可变对象。您将拥有fillWithColor()
功能,还有drawLine()
,drawElipse()
,drawText()
等等。使用这些功能构建图形可能需要多次调用。考虑绘制一个“no parking”流量标志:
如果您的Canvas
类是不可变的,则需要五倍的内存量并处理五倍像素数。这真是一个微不足道的例子。考虑猎豹的this SVG图像。背面的每个位置都是对绘图功能的调用。
取决于
我会说你应该尽可能使用不可变类型,并且在不可以的地方使用不可变类型。通常,这种差异沿着小数据类型而非大数据类型。
如果您的类型引用的数据结构小到足以成为值类型,那么它应该是一个不可变的引用类型。就像java Date
应该是不可变的一样,毕竟它只有8个字节。
如果你的类型引用了一些大的东西,并且你需要对它进行许多操作,那么你必须务实并使它成为一个可变类型。与您的Canvas
示例一样,在所有图像都可以是兆字节之后。
需要可变和不可变。