System.out.printlnに配列を渡すことから始まるtoString()の話

配列を作って、その中身を表示しようと思って、

```java:配列をそのままprintlnに
int[] array = {1,2,3,4,5};
System.out.println(array);
```

のように、配列をそのまま引数にして標準出力に渡してしまう人、初心者で意外にいるようだ。
この出力結果(例)は

```plaintext
[I@1540e19d
```

のように、わけのわからないものになる。
これを見て、「うまく動かない」などと質問されることが…
なぜこんなことになるのか？

# System.out.printlnとは
System → [java.lang.Systemクラス](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/System.html)
out → SystemクラスのPrintStream型のstatic変数
つまり、printlnは**PrintStreamクラスのメソッド**なのである。
そこでPrintStream#printlnを見てみると、いくつかのオーバーロードが存在する。
その多くは各種プリミティブ型に対応するためのオーバーロードなので、それ以外のものを挙げると、

println(char[] x)
println(Object x)
println(String x)

これらが存在する。プリミティブ型を含め、これらは引数を文字列化したものを出力する。
char[]の場合は各要素の文字をつなげて出力し、Stringもその内容をそのまま出力する。

Objectの場合はどうかというと、**引数を`String.valueOf`に渡した結果の文字列**が出力される。

###[String.valueOf(Object)](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/String.html#valueOf-java.lang.Object-)

> Object引数の文字列表現を返します。
パラメータ:
obj - Object。
戻り値:
引数がnullの場合は"null"に等しい文字列。それ以外の場合はobj.toString()の値が返される。

そのまま`toString()`にしないのはnullセーフのため。
ではその`toString()`とはどうなっているのか？

###[Object#toString](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Object.html#toString--)
> オブジェクトの文字列表現を返します。一般に、toStringメソッドは、このオブジェクトを「テキストで表す」文字列を返します。この結果は、人間が読める簡潔で有益な情報であるべきです。すべてのサブクラスで、このメソッドをオーバーライドすることをお薦めします。
クラスObjectのtoStringメソッドは、オブジェクトがインスタンスになっている元のクラスの名前、アットマーク文字「@」、およびオブジェクトのハッシュ・コードの符号なし16進数表現から構成される文字列を返します。つまり、このメソッドは次の値と等しい文字列を返します。

> getClass().getName() + '@' + Integer.toHexString(hashCode())
 
> 戻り値:
このオブジェクトの文字列表現。

となっている。ここで最初の出力を見てみよう。

```plaintext
[I@1540e19d
```

お分かりだろうか？この@以降の文字列は`Integer.toHexString(hashCode())`の結果、つまり**ハッシュコードの16進表記**ということである。しかし、この数字は見ても人にとっては意味がない。

メソッドの説明に「この結果は、人間が読める簡潔で有益な情報であるべきです。すべてのサブクラスで、このメソッドをオーバーライドすることをお薦めします。」とある通り、オーバーライドして使うのがこのメソッドの本来の使い方である。基底クラスであるObjectでは「とりあえず呼ばれたらこう出力する」とだけ決めてあって、**内容がどうこうといった情報は入っていない**のである。

#@の前の文字列 - 何のクラスのオブジェクト？
1つだけ有益な情報があるとすれば、それは**@の前の文字列**である。この例で言えば`[I`だ。
これが何…？そう思ったら、もう一度説明を読もう。
@の前の文字列、それは`getClass().getName()`の結果である。
ではそちらはどういう仕様になっているのか？

getClass()の返り値の型Classについては詳しく説明するのも理解するのも難しいので、とりあえず「どのクラスのインスタンスかを表すオブジェクト」という程度に認識してもらいたい。



###[Class#getName](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Class.html#getName--)
> このClassオブジェクトが表すエンティティ(クラス、インタフェース、配列クラス、プリミティブ型、またはvoid)の名前を、Stringとして返します。
『Java(tm)言語仕様』で規定されているように、このクラス・オブジェクトが配列型ではない参照型を表す場合は、クラスのバイナリ名が返されます。

> このクラス・オブジェクトがプリミティブ型またはvoidを表す場合、返される名前はプリミティブ型またはvoidに対応するJava言語キーワードと等価なStringです。

> このクラス・オブジェクトが配列のクラスを表す場合、名前の内部形式は、配列の入れ子の深さを表す1つ以上の[文字、要素のタイプの名前という順序で構成されます。要素のタイプの名前のエンコーディングは、次のとおりです。

> 要素のタイプ	   	エンコーディング
boolean型	   	Z
byte	   	B
char	   	C
classまたはinterface	   	Lclassname;
double	   	D
float	   	F
int	   	I
long	   	J
short	   	S
クラス名またはインタフェース名のclassnameは、上記の例のようにクラスのバイナリ名で指定されます。

> 例

> String.class.getName()
     returns "java.lang.String"
 byte.class.getName()
     returns "byte"
 (new Object[3]).getClass().getName()
     returns "[Ljava.lang.Object;"
 (new int[3][4][5][6][7][8][9]).getClass().getName()
     returns "[[[[[[[I"

簡単に言えば、そのインスタンスの型を表現する文字列である。
注目してほしいのは配列の時の場合。配列の次元数だけ`[`が並び、そのあとにクラス名(プリミティブ以外ならLが挟まる)が続く形式となっている。
例の最後の場合、配列が7次元になっているため、`[`が7個並び、そのあとにint配列であることを表すIが連なって表現されている。
つまり`[I`は、このオブジェクトが1次元のint配列である、ということを表現していたのだ。

以上のように、最初の一見意味不明な出力は、きちんと仕様に従って出力されていたものなのだ。意味不明な文字列だからといってバグと決めつけるのではなく、きちんと本来の動きを調べよう。
ちなみに、配列の中身をきちんと出力したいのなら、Arrays.toString()(多次元配列ならArrays.deepToString())に渡して文字列化するとか、ループを使って出力するとかしましょう。