質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

7回答

11773閲覧

int[]からInteger[]へのキャストができない理由

zoemond

総合スコア50

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

6グッド

3クリップ

投稿2016/05/12 01:43

編集2016/05/12 06:38

###知りたいこと

intからIntegerへのキャストはできるのに、int[]からInteger[]へのキャストができません。
Integerはintをオブジェクトとして扱うためにあるものならば、配列のキャストができた方がいいし、言語としても整合性がある気がしてしまいます。

どうしてそのような仕様にしたのでしょうか。
なにが不都合なのでしょうか。

###調べたこと①
Integer[]は値オブジェクトへの参照で、int[]はプリミティブの値なのでそもそも違うもの同士はキャストできないという理由は調べました。
なぜそうしたのかが知りたいです。
Integerはintをオブジェクトとして扱うためにオートボクシングしてくれる機能を付けたのに、配列は変換しないようにしたということが納得できません。

###調べたこと②
後方互換性、処理効率という話も少し出てきました。

後方互換性については具体的に何がだめなのかイメージできません。

処理効率にいては、int[]からInteger[]へ変換するときは結局ひとつひとつintの値をIntegerに入れるのだから、あらかじめ変換の機能を付けてもらった方がいいと考えてしまいます。

###追記:私の理解(間違っている可能性あり)

プリミティブ型:スタック領域に値を保持している
参照型:ヒープ領域に値があり、スタック領域から参照する
参照:値やオブジェクトを指す番号
配列:メモリ領域に連続して要素が並んでいる。a[0]の参照が分かればa[1]は次の参照にある。参照型。
ラッパークラス:プリミティブ型をオブジェクトとして扱えるようにする。参照型。

プリミティブ型→ラッパークラス//OK・・・・①
プリミティブ型の配列→ラッパークラスの配列//できない・・・②

stereo_code, yodel, A-pZ, tonarino210, LouiS0616, maisumakun👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答7

0

配列の要素に使っている型同士に継承関係があるなら、配列を別の型の配列に代入することはできます。
しかし、これはあくまで「この配列、A型が入っているんだからその親クラスのB型としても使えるね」という理由で入れているに過ぎません。(蛇足ですが、これはタイプセーフではなくなり、Javaの失敗とも言われている→参考:Java配列メモ | 共変)

int配列は仰るとおりプリミティブの値が入っており、このままではIntegerというオブジェクトとして扱うことはできません。オートボクシングではオブジェクトを要求されている所にint値が渡ると自動でオブジェクトに変換させて事なきを得ているのですが、intの配列に入っているint値をIntegerに変換しても、**intの配列にIntegerを入れることができません。**そのため、別個にIntegerの配列を新たに作成する必要が生まれます。

しかし、そもそものことを思い出して欲しいのですが、オブジェクトに対して"="は参照の代入を意味します。ということは、参照の代入をして2つの配列変数は同じ配列オブジェクトを指すはずなのに、違う配列を作成して別のオブジェクトを作成しなければならないというのは整合性が取れません。そもそもただの代入で新たに配列を作成するほどJavaはおせっかいではありません。

同じ理由で、Integer配列をint配列に変換させようとしてIntegerをアンボクシングしてintにしても、Integerというオブジェクトを扱う配列にint値を置くことができないため、変換できません。

以上の理由で、int配列⇔Integer配列の変換は認められていないのだと思います。

投稿2016/05/12 02:26

編集2016/05/12 02:38
swordone

総合スコア20651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zoemond

2016/05/12 09:18 編集

int→Integerの変換ができてint[]→Integer[]の変換ができないというのは整合性がないように見えるという質問に対して、参照の代入を表す=の整合性を保つためという説明でかなり分かりそうだったのですが、少しだけもやもやがあります。 用語やイメージがあっているのか自信がないのですが、 intの値がある領域の参照をIntegerに代入できる(オートボクシングではオブジェクトを要求されている所にint値が渡ると自動でオブジェクトに変換させて事なきを得ている)なら int[]の値が並んでいる領域の一番初めの参照もInteger[]に代入できる(intの配列にIntegerを入れることができる)のではないでしょうか。 どうなのでしょうか。 補足のようなものを質問欄に追記しました。 追記・補足:新たに配列を作らなければいけないというところがわかりません。すみません。
swordone

2016/05/12 16:23

そもそも「配列」とは、その型の変数を複数個まとめ、番号でその変数を識別できるようにしている仕組みです。 intの配列はint型の変数を、Integerの配列はIntegerの変数をまとめて扱っています。そしてそれぞれの配列としての変数は、その配列がある場所を記憶します。 intはプリミティブ型です。値そのものをその変数が記憶します。 Integerは参照型です。そのオブジェクトが存在する場所を変数が記憶します。 intの配列の変数をIntegerの配列の変数に入れようとしたとしましょう。参照の代入なので、それぞれが「同じintの配列」のある場所を記憶する必要があります。 しかし、Integer配列の変数に渡された場所にあったのはintの配列です。Integerの配列は、この「値そのもの」という要素をIntegerとして扱うことはできません。 それならばと、各要素をオートボクシングでIntegerに変換したとしましょう。変換はできるでしょう。しかし、Integerの配列としては配列の要素としてIntegerを持っていなければなりません。変換した要素を配列に入れようとする際、配列の実体はintの配列なので、Integerを入れることはできません。 このようにintの配列とIntegerの配列とでは性質が全く異なるため、intの配列をIntegerの配列として扱うのは無理なのです。 「じゃあプリミティブの配列のまま、読み込むときにオートボクシングで変換すればいいじゃないか」?ただの配列にそんな芸当はできません。配列はあくまで、ただの変数の集まりなのですから。
zoemond

2016/05/16 03:59 編集

なぜint→Integerは中身が違うのに許されているのか、配列だとなぜだめなのかが疑問点でしたが、いろんな方の回答から自己解決しました。 丁寧に回答していただきましてありがとうございました。
guest

0

なぜそのような仕様にしたのかは仕様策定された方々に聞く内容ではないかと思いますが、一発で変換する方法としては、Commons-LangのArrayUtilsを使えば、プリミティブ配列←→ラッパー配列の変換は可能です。
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/ArrayUtils.html

toArrayやtoPrimitiveメソッドで変換します。

ただし配列の内容によっては実行時例外になります。

あくまで予想ですが、型の変換についてはコンパイラレベルでエラーの有無を判定すべき、という判断があったのではないかと。

投稿2016/05/12 07:39

A-pZ

総合スコア12011

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zoemond

2016/05/12 08:42 編集

Commons-Langはすごいですね。使います。 このような仕様にした理由の正解は作った人しか分かりませんが、もっともらしい理由なら予想できるのではないかなと思いました。 型の安全性をどこで妥協するかを考えると、プリミティブ配列→ラッパー配列は許してもいいのではと思いました。(Commons-LangでいうtoOblectメソッドです) システムを作ったことは1回もありませんが、ジェネリクスクラスの引数にプリミティブ配列を入れたい事とか結構ありそうだと予想したりして、なぜこのような仕様にしたのかが気になりました。
guest

0

配列を配列にキャストする場合、要素同士がキャストできればキャスト可能となっています。

ところが、intIntegerの変換はオートボクシングという別なメカニズムによるもので、キャストはできません(オートボクシング自体があとからJavaへ追加されたこともあって、キャストとは別枠になっています)。

ということで、「要素同士のキャストができない」intIntegerの組み合わせでは、配列もキャストできません。

投稿2016/05/12 02:03

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zoemond

2016/05/12 02:31 編集

回答ありがとうございます。 キャストという言葉を正しく使えていませんでした。 int→Integerの変換をつけて、int[]→Integer[]へのオートボクシング機能を付けなかったのはなぜでしょうか。
guest

0

簡単に言うと、これが出来ないからです。

Java

1Integer[] array1 = new Integer[]{1,2,null,4};// これはOK 2int[] array2 = new int[]{1,2,null,4};// これはNG

投稿2016/05/12 02:02

abs123

総合スコア1280

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zoemond

2016/05/12 02:24 編集

Integer[]=int[]; ができないのはなぜですか? 回答していただいたのにすぐ理解できずに申し訳ないです。 nullのないint[]からInteger[]への変換はできるような気がしてしまいます。
abs123

2016/05/12 02:48

Integer[]→int[]変換が出来ないのに、 int[]→Integer[]変換が出来るようにしてしまうと、 それこそ整合性が取れていないということになってしまわないでしょうか? まぁ、結論的に言ってしまえば、 Javaではそういう仕様だから出来ない、 で最終的に理解してもらうしかないと思いますね。
zoemond

2016/05/12 05:39

int とlongのような関係を考えれば、変換が一方通行であるということはそこまで整合性がないということにはならないと感じます。 Javaの仕様としてできることできないことがあることは分かるのですが、なぜそのような機能にしたのかということが知りたいです。 質問に答えていただいてありがとうございます。
guest

0

自己解決

そもそも、
Subclass → SuperClass //OK
Subclass[] → SuperClass[] //OK
int → Integer //OK
int[] → Integer[] //コンパイルエラー
という整合性のなさが気になるというのが質問した理由ですが
int → long //OK
int[] → long[] //コンパイルエラー
ということを知りませんでした。
このint[] → long[] の関係を考えれば、プリミティブ配列に関してはswordoneさんや HiroshiWatanabeさんの回答の通り、中身の型を一致させなければいけないという理由で、
int[] → Integer[] //コンパイルエラー
ということの整合性はあっていることに気付きました。
そして、どうして配列は中身の型を一致させなければいけない仕様にしたかという点については、インスタンス生成の効率面を考えてのことだというRyotaKondoさんの解釈で納得しました。

いろいろな方に回答をしていただいて、それがまとまって納得したので、自己解決という形にします。

質問の仕方や、質問する側の知識がまとはずれで、回答しづらくしてしまい申し訳ありませんでした。
また、ちょっとめんどくさい疑問だったと思いますが、すぐにいろいろな方に助けていただけたのでびっくりしました。ありがとうございました。

投稿2016/05/16 03:54

zoemond

総合スコア50

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

「なぜそんな仕様にしたのか」
という観点から私なりの考えを書いてみます。
int[]⇒Integer[]
だったらそのまま数字を入れればできる。
Integer[]⇒int[]
なら、できるときはして、できないnullがあったらエラーでも出して・・・
という考え方で仕様にすれば良いということですよね。

おそらくそんな仕様にしてくれれば「プログラムを書く人間からすれば素晴らしく楽なこととなるのに」といったところでしょうか?

では、そのような世界でどのようなことが行われるかをかんがえてみましょうか。
次のような配列があるとします

java

1int[] a = new int[3]; 2Integer[] b = new Integer[3];

まあ、この時点でaの中身は{0,0,0}、bの中身は{null,null,null}ですね。
この違いは許容しましょう。

java

1a=b;

これはエラーを出しましょう。

java

1b=a;

これは・・・プログラムとしては
Integer[]を作成。aの各数字をオートボクシングして作成です。
つまり、事実上

b=new Integer[a.length]; for(int i=0;i<a.length;i++){ b[i]=Integer.valueOf(a[i]); //又はb[i]=new Integer(a[i]); }

を自動でやってくれます。

ところで次のコードを考えてみましょう。

java

1List<Integer> list; 2......... 3int[] a=new int[3]; 4for(int i=0;i<999;i++){ 5 a[0]=i; 6 a[1]=i*2; 7 a[2]=a[0]+a[1]; 8 Collections.addAll(list,a[0],a[1],a[2]); 9} 10

aがInteger配列ではない理由はループ内で計算をするためです。
int[]⇒Integer[]が可能であれば

java

1int[] a=new int[3]; 2for(int i=0;i<999;i++){ 3 a[0]=i; 4 a[1]=i*2; 5 a[2]=a[0]+a[1]; 6 Collections.addAll(list,a); 7} 8

でよくなります。

ここで、少し考えてみましょう。
addAllに「配列内の1つずつを入れた場合」と「配列自体を入れた場合」どちらが処理として重いでしょう?
「配列自体を入れた場合」のほうが重くなります。
理由は、実質new Integer[a.length]という処理がループ中に増えるからです。

一つくらいいいじゃない?と思われるかもしれませんが、

java

1Collections.addAll(list2,a); 2Collections.addAll(list3,a); 345

という文がループ内に更にあったとしたら?
処理は多くなりますよね?

私はここにその仕様の理由があると考えています。

ちなみに通常の、オートボクシング、アンボクシングに対してもこのようなループ中に行う場合はそれなりの負荷がかかるので、使いドコロには注意が必要です。

投稿2016/05/13 18:33

RyotaKondo

総合スコア94

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

気にしたこと無かった&私も知らなかったので今回の回答で一緒に勉強させてもらっています。
まとめるとこういう事でしょうか…?

Q1.int→Integerができるのにint[]→Integer[]ができないのは何故か?(int→Integerのオートボクシング)
A1.int[]→Integer[]ができないのはInteger[]→int[]ができないから片方(逆向き)だけできると整合性が取れないため

Q2.Integer→intができるのにInteger[]→int[]ができないのは何故か?(Integer→intのアンボクシング)
A2.Integer→intのアンボクシングができるのはIntegerがnullでない事が前提
(nullなIntegerをintにしようとすればNullPointerExceptionが発生する)
つまりInteger→intはIntegerがnullでない時に限定してのみintにできる
Integer[]→int[]の場合は配列の中身にnullが入っている可能性があるのでint[]にできる保証が無い=できない

投稿2016/05/12 08:10

HiroshiWatanabe

総合スコア2160

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zoemond

2016/05/12 08:18

ありがとうございます。 int in = 0; long lo = 0; in = lo;//コンパイルエラー lo = in;//OK ということは、片方だけのキャストができると考えているのですがどうでしょうか。 この通りの整合性で行くと、キャストが一方通行でもいいのでは・・・と感じるのですが・・・
HiroshiWatanabe

2016/05/12 09:11

例えば int[] a = new int[5]; int[] b = a; な関係を考えた時、bはaを指しているので実体としては「同じもの」ですよね? (b[1]を変更すればa[1]を変更したのと同じことになる) 仮に Integer[] c = a; が可能だとするなら、c は a と同じものを指している理屈になりますよね? でもInteger[] はIntegerというオブジェクトの配列であってintの配列ではないためInteger[]の中には元のint[]の中に入っていたint値自身とは別の(そのint値を持っている)Integerオブジェクトを生成して配列にした物…でなくてはならない理屈になりますよね? となると元のaという配列とはまるっきり別の配列という事になるのでint[]同士の参照(代入)とは扱いを同一視できなくなってしまいます。 整合性が取れないというのはそういう点も考慮しての事では無いでしょうか。
zoemond

2016/05/16 03:58

なぜint→Integerは実体が違うのに許されているが、配列だと許されないことの整合性が疑問でしたが、自分なりに解決しました。 丁寧に回答していただきましてありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問