イミュータブル |
不変という日本語訳からも分かる通り、イミュータブルはオブジェクトの生成
時に設定された属性を変更することができないオブジェクトの性質です。
一見、属性を後から変更することができない性質はデメリットであるようにし
かみえません。
class Figure {
....
public void move(double x, double y) {
region_.x = x;
region_.y = y;
}
Region region_;
}
|
としたいところを
class Figure {
....
public void move(double x, double y) {
region_ = new Region(x + region_.getX(), y + region_.getY());
}
Region region_;
}
|
としなければならないのはあまりにもムダと思えるものです。
しかしイミュータブルにはこの欠点を補ってあまりあるメリットがあるのです。
オブジェクトをイミュータブルとすることのメリットは以下の2つ。
Javaではjava.lang.Stringがイミュータブルの典型例です。
java.lang.Integerやjava.lang.Floatなどのラッパクラスや、java.awt.Color
といったところもイミュータブル。
イミュータブルはすでにJavaのプログラミングテクニックとして広く利用され
ていることが分かります。
public class Region {
public Region(double x, double y, double width, double height) {
x_ = x;
y_ = y;
width_ = width;
height_ = height;
}
public final double getX() {
return (x_);
}
public final double getY() {
return (y_);
}
public final double getWidth() {
return (width_);
}
public final double getHeight() {
return (height_);
}
private double x_;
private double y_;
private double width_;
private double height_;
}
|
イミュータブルオブジェクトの使用例として、Regionを属性値として持つオブ ジェクトFigureを考えてみます。
public class Figure {
public Figure(Region region) {
region_ = region;
}
public Region getRegion() {
return (region_);
}
private Region region_;
}
|
一見ごく普通の実装に見えますね。
しかしイミュータブルでない場合と比較すると違いは明らかになります。
public class Region implements Cloneable {
public Region(double x, double y, double width, double height) {
x_ = x;
y_ = y;
width_ = width;
height_ = height;
}
public final synchronized double getX() {
return (x_);
}
public final synchronized double getY() {
return (y_);
}
public final synchronized double getWidth() {
return (width_);
}
public final synchronized double getHeight() {
return (height_);
}
public final synchronized void setValues(
double x, double y, double width, double height
) {
x_ = x;
y_ = y;
width_ = width;
height_ = height;
}
private double x_;
private double y_;
private double width_;
private double height_;
}
|
さらに影響があるのがオブジェクトを使う時。
public class Figure {
public Figure(Region region) {
region_ = region.clone();
}
public Region getRegion() {
return (region_.clone());
}
private Region region_;
}
|
Regionオブジェクトのクローンをしまくっています。
これは以下のような使われ方をしても誤動作しないため。
イミュータブルにしないということは、非常にコストのかかることなのです。
Region myRegion = new Region(0, 0, 10, 10);
Figure figure = new Figure(myRegion);
myRegion.setValue(100, 100, 10, 10); //★もしcloneしていないとFigureのRegion値が変ってしまう
Region anothorRegion = figure.getRegion();
anotherRegion.setValue(10, 50, 200, 100); //★もしcloneしていないとFigureのRegion値が変ってしまう
|
public class Region implements Cloneable {
public Region(double x, double y, double width, double height) {
x_ = x;
y_ = y;
width_ = width;
height_ = height;
}
public final synchronized double getX() {
return (x_);
}
public final synchronized double getY() {
return (y_);
}
public final synchronized double getWidth() {
return (width_);
}
public final synchronized double getHeight() {
return (height_);
}
public final synchronized void setX(double x) {
x_ = x;
}
public final synchronized void setY(double y) {
y_ = y;
}
public final synchronized void setWidth(double width) {
width_ = width;
}
public final synchronized void getHeight(double height) {
height_ = height;
}
private double x_;
private double y_;
private double width_;
private double height_;
}
|
複数の属性(この場合は4つ)を同時に変更する場合、相互排除を考慮すると以
下のようにする必要があります。
マルチスレッド環境では属性の値を変更するのもひと苦労。
Region myRegion = new Region(0, 0, 10, 10);
synchronized (myRegion) {
myRegion.setX(20);
myRegion.setY(50);
myRegion.setWidth(100);
myRegion.setHeight(70);
}
|
| コーナー | 記事 | 関連 |
|---|---|---|
| Java Tips | カージナルオブジェクトの設計 | カージナルオブジェクトとエンティティオブジェクトはイ ミュータブルの候補 |
| エンティティオブジェクトの設計 |