Javaには値の型として、基本データ型と、参照型が存在する。それぞれに関して説明していく。
基本データ型と参照型とは
基本データ型
型名 | bit数 | 内容 |
---|---|---|
boolean | 1bit | 真偽値 |
byte | 8bit | 符号あり整数値 |
char | 16bit | 符号なし整数値 |
short | 16bit | 符号あり整数値 |
int | 32bit | 符号あり整数値 |
float | 32bit | 浮動小数値 |
long | 64bit | 符号あり整数値 |
double | 64bit | 浮動小数値 |
の八つ。基本データ型の変数は、型によって必要なbit数が決まっている。これらの型の値を保持する変数を基本データ型変数という。
参照型
値そのものを保持するのではなく、値が置いてある場所を保持する。
そのように、値が置いてある場所を示す(参照型の)値を保持する変数を 参照型変数という。
# 基本データ型と参照型の値の代入の違い
基本データ型と参照型を正しく使い分けることが大事です。特に、どちらの型の代入が行われているのかを意識することです。以下、例と共に説明します。
基本データ型の値の代入
以下のコードをみてください。
int a = 1;
int b = a;
b = 2;
System.out.println(a); // => 1を返す
基本データ型変数a,bには、基本データ型値がセットされます。
2,3行目でbに新しい基本データ型値をセットしていますが、もちろん、aの基本データ型値は変わらないので、aは1を持ち続けています。
これは直感的に明らかでしょう。問題は次の参照型にかんしてです。
参照型の値の代入
基本データ型と比較して、以下のコードをみてください。
int a[] = {1};
int b[] = a;
b[0] = 2;
System.out.println(a[0]); // => 2を返す
参照型変数a,bには、参照型値がセットされます。
2行目でbに新しい参照型値をセットしていますが、もちろん、 aの参照型値は変わりません。ここで、bがaと同じ参照型値を持つようになります。3行目で、b[0]
という参照先の値を更新しています。これによって、bとaの参照型値が共に参照している先の値が書き換わっているのです。
つまり、 参照型値の代入では参照型値を書き換え、 参照先の値(基本データ型/参照型)の代入は、その参照先の値を書き換えます。
あたり前のことを言っているようにしか聞こえないのですが、参照型値の代入と、基本データ型の代入のどちらが起きているかわからずに混乱する人が多いのではないでしょうか。
ケーススタディ
どんな時でも、ポイントは、基本データ型の値を更新しているのか、参照型の値を更新しているのか。
代入時
以上の原則を踏まえ、他の例を見ていきましょう。
int a[] = {1};
int b[] = a;
b[0] = 2;
System.out.println(a[0]); // => 2を返す
System.out.println(b[0]); // => 2を返す
int c[] = {3};
b = c;
System.out.println(a[0]); // => 2のまま!!
System.out.println(b[0]); // => 3を返すようになる
ポイントは、b = c
では行われているのは、何型の値の代入か、ということです。もちろん、これは、参照型値の代入です。これまで、aの参照値を持っていたbですが、その bの参照値自体が、cの参照値に更新されました。そのため、参照値の指す先の値であるb[0]
はc[0]
と同じ3
を指すようになります。
ここで、 bの以前の参照値の指す先の値(3行目のb[0]
)自体が更新されたわけではないため、a[0]
は変わらず、2
を指すままとなります。
もうお判りかと思いますが、今度は、以下のように、参照先の値b[0]
を更新すると、同じ値を参照している参照型値を持つcの参照先の値c[0]
も同様に更新されることになります。
//...先ほどの続き
b[0] = 4;
System.out.println(a[0]); // => 2のまま!!
System.out.println(b[0]); // => 4を返すようになる
System.out.println(c[0]); // => 4を返すようになる
引数の時
引数においても、参照型変数の時は、参照値が渡されます。
以下、method1
では、array
と同じ参照値の値を持っていた変数a
の参照値そのものを代入し直しています。そのため、array
の指す値自体は変わらず、{0}
のままです。
一方、method2
では、array
と同じ参照値を持つ変数a
参照値の指す値を更新しているため、array
の指す値も{100}
になります。
static void method1(int[] a) {
int[] other = { 10 };
a = other;
}
static void method2(int[] a) {
a[0] = 100;
}
public static void main(String[] args) {
int[] array = { 0 };
System.out.println(array[0]); // => 0
method1(array);
System.out.println(array[0]); // => 0
method2(array);
System.out.println(array[0]); // => 100
}
返り値の時
同じことです。
返り値でも、(もちろん、)参照型変数の時は、参照値が返されます。
以下簡略化された例ですが、getArray()
は、参照型変数a
に参照値をセットするので、その値が指す先を100に更新すると、同じ参照値を持つarrayからアクセスされる値も100を返すようになります。
static int[] array = new int[10];
static int[] getArray() {
return array;
}
public static void main(String[] args) {
int[] a = getArray();
a[0] = 100;
System.out.println(array[0]); // => 100
}
参照型の特性を活かしたテクニック
基本データ型の値で、参照型のように参照値を引数として渡したいケースは、 ラッパークラスを作る、というテクニックがあります。
class IntWrapper {
public int i = 0;
}
こうすることで、いままで基本データ型の値を渡していたところで、参照値を渡すことができるようになります。すると、渡した先で、参照先の値を更新できる、という利点があります。
P.S. Rubyではポインタと参照なんか知らなくてもかけるわ〜〜、と思ってたけど,[ruby] ポインタと参照とか知らなくても ruby は書ける、なんてことはない。らしかったです。なるほどです
P.S. 正しい言葉の定義での突っ込み求む。参照型値など。。