Javaでオブジェクトの文字列表現を返すtoStringメソッド.
普段適当に使ってるけど中身の実装を調べてみた
はじめに(利用例)
public class Hoge{
@Override
public String toString(){
return "このクラスはHogeクラスです.";
}
}
public class Test1{
public static void main(String[] args){
Object hoge = new Hoge();
System.out.println(hoge);
}
}
こんな感じで,toStringメソッドを適当にオーバーライドしてあげると,
printの引数に入れてやるだけで上手いこと表示してくれる便利な機能ですね.
(文字列として解釈できる場合の演算も,上手いこと処理してくれます)
実装の確認
今回は,この実装を確かめようと思います.
まず,ドキュメントの説明から.
オブジェクトの文字列表現を返します。一般に、toString メソッドは、このオブジェクトを「テキストで表す」文字列を返します。この結果は、人間が読める簡潔で有益な情報であるべきです。すべてのサブクラスで、このメソッドをオーバーライドすることをお勧めします。
クラス Object の toString メソッドは、オブジェクトがインスタンスになっている元のクラスの名前、アットマーク文字「@」、およびオブジェクトのハッシュコードの符号なし 16 進数表現から構成される文字列を返します。つまり、このメソッドは次の値と等しい文字列を返します。
getClass().getName() + '@' + Integer.toHexString(hashCode())
toStringメソッドはObjectクラスで実装されていて,デフォルトでは
クラス名とハッシュコードを表示します.
Objectクラスは,Javaの全てのクラスのスーパークラスなので,
全てのクラスはtoStringメソッドを持っています.
toStringの使い道
toStringメソッドは普段こんな感じで扱われているのでは
と推察してみた.
- オブジェクトから文字列への変換 (明示的)
- 文字列結合演算子 (暗黙的)
この仮説が正しいか,確認をしてみよう.
オブジェクトから文字列への変換
例えば,System.out.println
の実装は,
- println(char[] x)
- println(Objext x)
- println(String x)
など,様々な引数に対応できるよう,オーバーロードさせて作っています.
そこで,Objectを引数とする場合の
System.out.printlnの実装を確かめると,
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
このように, Object#valueOf()を呼んでいます.
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
Stringクラスの実装を確かめると,
引数のオブジェクトがnullでない場合は, toStringを呼んでいます.
(NullPointer回避)
文字列結合演算子
文字列結合演算子による結合は,「Java言語仕様」によるため,
バージョンによって処理が異なります.
ややこしいので詳細は省きますが,文字列結合演算子+
による
コードは,裏ではStringBuilder.append()として扱われるようです.
イメージではこんな感じになってると思われます.
public class Test2{
public static void main(String[] args){
"time: " + new Date();
// new StringBuilder().append("time: ").append(new Date());
}
}
そこで,StringBuilderの実装を確かめにいきます.
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
あっ...
# ついでに
配列はリストの中身表示してくれないのにアレイリストは表示してくれるの
たまに間違えて大変だよね.
ArrayListのスーパークラス,AbstractCollectionが
toStringのオーバーライドしてた.
```java:AbstractCollection.java
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
```
どうやら,[配列は言語仕様によって定められているらしい](https://docs.oracle.com/javase/specs/jls/se10/html/jls-10.html#jls-10.7)
これを見ると,toStringメソッドをオーバーライドしていないのでそりゃそうなるわなって感じがある.
```java
class A<T> implements Cloneable, java.io.Serializable {
public final int length = X;
public T[] clone() {
try {
return (T[])super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e.getMessage());
}
}
}
```
初心者泣かせのlength,お前publicでfinalだったのか...
# 終わりに
toString便利.