- PR -

AutoBoxingの仕様について

投稿者投稿内容
yuzo
会議室デビュー日: 2007/08/27
投稿数: 5
お住まい・勤務地: 東京都
投稿日時: 2008-05-03 07:28
いつもお世話になっております。
現在、SJC-P5.0の資格取得に向けて勉強しておりますが、
AutoBoxing関連でわからない箇所がありましたので
ご教授をお願いします。

尚、使用しているJDKは1.5.0_12 です。

まず、下記コードをコンパイルして実行したら結果はTrueが返ります。
class Test{
public static void main(String[] args){
Integer i = 10;
Integer j = 10;

if(i==j){
System.out.println("True");
}else{
System.out.println("False");
}
}
}

しかし、INTEGERの部分を1234に変更するとFalseが返ってきます。
class Test{
public static void main(String[] args){
Integer i = 1234;
Integer j = 1234;

if(i==j){
System.out.println("True");
}else{
System.out.println("False");
}
}
}

今まではIntegerにint型を代入する際にはnew Integerとしてきましたが、
5.0からはAutoBoxing機能によってそのまま代入できるようになっております。
1234はint型の範囲に入っておりますので問題なく結果はTrueと推測しておりましたが、
実際の結果が違います。

なぜ、このような結果になるかご教授して頂けないでしょうか?
よろしくお願いします。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2008-05-03 09:07
手前味噌ですがIntegerにはFlyweight が活きているあたりを参考に。

ボクシング変換で大量にIntegerオブジェクトを生成することになるため、
-128から127までの範囲でオブジェクトがキャッシュされるんです。

javadocにもキャッシュされる旨が書かれていますね。
引用:

指定した int 値を表す Integer インスタンスを返します。新規 Integer インスタンスが不要な場合、通常このメソッドがコンストラクタ Integer(int) に優先して使用されます。 その理由は、このメソッドが頻繁に要求される値をキャッシュするので、操作に必要な領域や時間がはるかに少なくて済む場合が多いためです。


nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2008-05-03 09:27
引用:

yuzoさんの書き込み (2008-05-03 07:28) より:
5.0からはAutoBoxing機能によってそのまま代入できるようになっております。
1234はint型の範囲に入っておりますので問題なく結果はTrueと推測しておりましたが、
実際の結果が違います。



AutoBoxingでは「そのまま代入できる」わけではありません。
コード:
Integer i = 10;


とした場合、実際には
コード:
Integer i = Integer.valueOf(10);


が実行されます。AutoBoxing、つまり自動型変換ですね。
これはコンパイラが勝手にこうしたコードに置き換えてくれるわけで、
intにIntegerを、あるいはIntegerにintを「そのまま代入できる」わけではない。
細かな挙動を理解するにはこのあたりを注意しておく必要があります。

intとInteger間での型変換が自動でされるだけで、Integerはやはりオブジェクト型です。
==での比較は、そのオブジェクトの参照の等しさを比較しますから、

コード:
Integer i = new Integer(10);
Integer j = new Integer(10);
if (i==j){
  System.out.println("True");
}else{
  System.out.println("False");
}


とした場合、"False"が表示されます。
「問題なく結果はTrueと推測しておりました」とおっしゃっていますから
オブジェクトの==比較とequals()による比較が混同されているように伺えます。

if (i==j)の部分は、Integer型同士の==比較です。
双方をint型に変換してからの==比較とはなりません。
そして、先に示したように、-128から127までの範囲では
オブジェクトがキャッシュされるため、同一のインスタンスが用いられますから
==でオブジェクトの参照の等価性を見た場合にtrueになりますし、
その範囲外の場合はnew Integer()され、別のインスタンスが作られるためにfalseとなります。
yuzo
会議室デビュー日: 2007/08/27
投稿数: 5
お住まい・勤務地: 東京都
投稿日時: 2008-05-03 10:16
nagise様

早急なご回答ありがとうございます。

オブジェクトがキャシュされているなんて想像ができませんでした。
再度、AutoBoxingを見直しして自動変換であることと実際には
「Integer i = Integer.valueOf(10);」が実行されていることを
確認しました。

上記を確認した場合では確かにFalseになるのもうなずけます。

開発的には注意しないといけないと感じましたし、この辺で
不具合とかでそうな気がしております。
コード的には「Integer i = Integer.valueOf(10);」を記載した方が
いいのかなぁ〜と実感しております。

ありがとうございます。
もっとJavaにのめり込んでいきます。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2008-05-03 12:19
引用:

開発的には注意しないといけないと感じましたし、この辺で
不具合とかでそうな気がしております。


Integer同士を==で比較する事自体が不具合の原因となります。
equalsで比較してください。これはAutoBoxingとは関係ありません。

逆に不具合の原因となりやすいのは逆のパターンです。
コード:
Integer integer = null;
int i = integer;


この場合、iに代入するときにNullPointerExceptionとなります。
yuzo
会議室デビュー日: 2007/08/27
投稿数: 5
お住まい・勤務地: 東京都
投稿日時: 2008-05-03 15:21
かつのり様

ご回答ありがとうございます。
確かに==では確認しないですよね
大変失礼しました。

class Test{
public static void main(String[] args){
Integer integer = null;
int i = integer;
}
}

上記で確認を行い、NullPointerExceptionで確認しました。
でも開発的にはラッパーにしない方が無難と感じております。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2008-05-03 17:30
ListやMapとか使うと、自然とラッパー型を使わざるおえない状況になります。
コード:
List<Integer> list = new ArrayList<Integer>();
list.add(1);  //intの1を格納しているが、実際にはIntegerの1が格納される
list.add(2);
list.add(3);

//Integerの1が格納されているが、intに変換される
for(int i : list){
    System.out.println(i);
}


という感じで、表には出ませんが、内部ではIntegerで格納されます。

とにかく、何が悪いだの使わない方がいいだのと、むやみに決めずに、
何をするとどうなるかをキチンと理解して使えば、副作用なしで使えます。
わたなべ
大ベテラン
会議室デビュー日: 2007/12/09
投稿数: 123
お住まい・勤務地: 札幌
投稿日時: 2008-05-03 23:34
SJC-Pを受けるという話が前提であるならば、Boxingの所はかなりマニアックな内容が出てきます。
したがって、合格を念頭に考えるのであれば、おそらく現在の理解でも問題ないでしょう。

Integerの-128から127までは〜なんて仕様は通常は意識しませんし、Javaを長くやっている開発者でもSJC−Pの勉強をした人か言語マニアでない限り、ずっと知らないままです。
ですが、== とequalsの違いは重要なのでしっかり学んでください。

ちなみに合格するにはBoxingよりもGenericsを深く学んだほうが高スコアが狙えると思います。

スキルアップ/キャリアアップ(JOB@IT)