一般的な不変クラスに適用される通常の理由を知っています。
ただし、ラッパークラスはプリミティブ型を表し、プリミティブ型は変更可能です。では、なぜラッパークラスは変更できないのでしょうか。
ただし、ラッパークラスはプリミティブ型を表し、プリミティブ型(Stringを除く)は変更可能です。
まず、Stringはプリミティブ型ではありません。
第二に、プリミティブ型が可変であることについて話すのは意味がありません。 変数の値を次のように変更した場合:
int x = 5;
x = 6;
これは数値5を変更するのではなく、x
の値を変更します。
ラッパータイプを変更可能にすることもできましたが、私の見解では、変更するのは面倒でした。私はこれらのタイプの読み取り専用コレクションを頻繁に使用しますが、変更可能にしたくありません。非常にまれに、変更可能な同等のものが必要ですが、その場合は、1つを考え出すか、Atomic*
クラスを使用するのは簡単です。
Date
とCalendar
が不変であることが、Integer
が可変であることを望んでいるよりもはるかに頻繁にあることを望んでいます...(もちろん、私は通常、代わりにJodaTimeに到達します、しかしJoda Timeの利点の1つis不変性。)
一部のタイプには、変更可能なスレッドセーフラッパーもあります。
AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicLong
AtomicLongArray
AtomicReference - can wrap a String.
AtomicReferenceArray
さらに、いくつかのエキゾチックなラッパー
AtomicMarkableReference - A reference and boolean
AtomicStampedReference - A reference and int
参考:可変ホルダークラスが必要な場合は、Java.util.concurrent
パッケージのAtomic *クラスを使用できます。 AtomicInteger
、AtomicLong
これは、整数が可変である場合に非常に悪い例です。
class Foo{
private Integer value;
public set(Integer value) { this.value = value; }
}
/* ... */
Foo foo1 = new Foo();
Foo foo2 = new Foo();
Foo foo3 = new Foo();
Integer i = new Integer(1);
foo1.set(i);
++i;
foo2.set(i);
++i;
foo3.set(i);
現在、foo1、foo2、およびfoo3の値はどれですか?これらは1、2、および3であると予想されます。ただし、Integerが可変である場合、Foo.value
はすべて同じIntegerオブジェクトを指すため、これらはすべて3になります。
ただし、ラッパークラスはプリミティブ型を表し、プリミティブ型(Stringを除く)は変更可能です。
いいえ、そうではありません(そして、Stringはプリミティブ型ではありません)。しかし、プリミティブ型はとにかくオブジェクトではないので、そもそもそれらを可変/不変と呼ぶことはできません。
とにかく、ラッパークラスが不変であるという事実は、設計上の決定です(優れたIMOです)。ラッパークラスは簡単に変更可能にするか、変更可能な代替手段も提供することができます(実際、いくつかのライブラリがこれを提供し、他の言語はデフォルトで提供します)。
を持っているすべてのオブジェクトインスタンス どれか 可変アスペクトには固有のものが必要です 身元;そうしないと、ある時点でそのIDを除いてすべての点で同一であった別のオブジェクトインスタンスが、別の時点でその可変の側面で異なる可能性があります。ただし、多くの場合、IDを持たない型の場合、心配することなく「4」を渡すことができると便利です。 which 「4」のものが通過しています。プリミティブタイプまたは不変タイプの可変ラッパーがあると便利な場合もありますが、ある時点で同じデータを保持するすべてのインスタンスが次のように見なされるタイプがあると便利な場合もあります。交換可能。
ラッパークラスは、変更可能であることが意味をなさないため、不変です。
次のコードを検討してください。
int n = 5;
n = 6;
Integer N = new Integer(n);
最初は、nの値を変更できるのと同じように、Nの値を変更できるかどうかは簡単に見えます。
しかし実際には、Nはnのラッパーではなく、6のラッパーです。次の行をもう一度見てください。
Integer N = new Integer(n);
実際には、nの値(6)をNに渡します。また、Javaは値渡しであるため、nをNに渡して、Nをnのラッパーにすることはできません。
したがって、ラッパーにsetメソッドを追加した場合:
Integer N = new Integer(n);
N.setValue(7);
print(N); // ok, now it is 7
print(n); // oops, still 6!
Nの値は変更されないため、混乱します。
結論:
ラッパークラスは値のラッパーであり、変数のラッパーではありません。
setメソッドを追加した場合は混乱します。
値のラッパーであることがわかっている場合は、setメソッドを要求しなくなります。たとえば、「6.setValue(7)」は実行しません。
javaで変数のラッパーを作成することは不可能です。
たとえば、次のJavaプログラム:
class WhyMutable
{
public static void main(String[] args)
{
String name = "Vipin";
Double sal = 60000.00;
displayTax(name, sal);
}
static void displayTax(String name, Double num) {
name = "Hello " + name.concat("!");
num = num * 30 / 100;
System.out.println(name + " You have to pay tax $" + num);
}
}
Result: Hello Vipin! You have to pay tax $18000.0
これは、ラッパークラスパラメーターの参照渡しの場合にも当てはまります。また、文字列とラッパークラスが非ファイナルの場合、誰でもそれらのクラスを拡張し、独自のコードを記述して、ラップされたプリミティブデータを変更できます。したがって、データの整合性を維持するために、データストレージに使用する変数は読み取り専用である必要があります。
つまり、文字列とラッパークラスは最終的で不変である必要があり、「参照渡し」機能は提供しないでください。
プリミティブ型は変更可能ですが、共有することはできません。つまり、コードの2つのセクションが同じint変数を参照することはありません(常に値で渡されます)。したがって、コピーを変更しても、他の人には変更が表示されません。その逆も同様です。フィリップが彼の答えで示しているように、それは可変ラッパークラスには当てはまりません。したがって、私の推測では、プリミティブデータ型を次のようにラップするときに選択肢がありました。
プリミティブ型の値を変更できるという事実と一致し、
versus
プリミティブ型を渡すことができ、ユーザーによる変更はデータの他のユーザーには表示されないという事実と一致します。
そして彼らは後者を選びました、それは不変性を必要としました。