倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

あんたに GDK の何が分かるっていうの!? (Object 編5) デバッグ用メソッド

今回は GDK が Object クラスに追加しているデバッグ用のメソッドを見ていきます。 主に print(), println() のような標準出力へメッセージを書き出すメソッドや、オブジェクトの情報を文字列に変換する toString(), dump(), inspect() などを扱います。 具体的には以下のメソッドを見ていきます:

// 標準出力への書き出し
void print(Object value)
void println()
void println(Object value)
void printf(String format, Object arg)
void printf(String format, Object[] values)

// PrintWriter への書き出し
void print(PrintWriter out)
void println(PrintWriter out)

// 文字列のフォーマット
String sprintf(String format, Object arg)
String sprintf(String format, Object[] values)

// オブジェクトの情報を文字列として返す
String toString()
String inspect()
String dump()

もう少し各メソッドを詳しく見ると

メソッド 返り値 since 説明
print(Object)
println()
println(Object)
printf(String, Object)
printf(String, Object[])
void 1.0 標準出力へメッセージを書き出す。
print(PrintWriter)
println(PrintWriter)
void 1.0 引数の PrintWriter へ文字列を書き出す
sprintf(String, Object)
sprintf(String, Object[])
String 1.5.0 フォーマットした文字列を返す
toString() String 1.0 Object#toString()。 ただし、GDK がオーバライドしているものもある
inspect() String 1.0 ターミナル上でそのオブジェクトを表現する文字列を返す
dump() String 1.0 オブジェクトのクラス、ハッシュコード、フィールドを示す文字列表現を返す。

inspect() や dump() は GroovyConsole とかでオブジェクトの内部情報を知りたいときなどに使うんじゃないかなぁ。 println(Object) と println(PrintWriter) は出力されるオブジェクトが引数かメソッドを呼び出されたオブジェクトかが異なるので注意(print() メソッドに関しても同じ)。 ではサンプルコードを見ていきましょう。

標準出力へ書き出す

まずは古き良きデバッグ方法、標準出力へメッセージを書き出すメソッド:

  • print(Object)
  • println()
  • println(Object)
  • printf(String, Object)
  • printf(String, Object[])

println() は改行のみ。 println(Object) は最後に改行も書き出します。 printf() は %s とか %d とかのプレースホルダーを使用してフォーマットを指定するやつです。 JDK 1.5 以降が必要です。

そういえば、Java では標準出力へ書き出すときに「System.out.println(...)」としないといけないけど、Groovy では「println(...)」だけでいいよ!という風に Groovy の簡潔さをアピールしていることがよくあるけど、この println() は GDK が Object クラス(よって全てのオブジェクト)に追加した println() メソッドを呼び出すようになってるんですよね。 なんというか、エレガントなような、力ずくなような実現方法っすねw 使い方に関してはあんまり説明いらないかと:

print 'Hello, GDK world!'
println()
println 'Hello, GDK world!'
printf "I am %s, %d years old.\n", 'waman', 100

最後の printf() では、配列やリストにしなくてもよさそうです。 これを実行すると以下のように表示されます:

Hello, GDK world!
Hello, GDK world!
I am waman, 100 years old.

printf() で使えるフォーマット指定は JavaDoc とか見てちょ。

PrintWriter へ書き出す

次は PrintWriter オブジェクトを指定して、メソッドを呼び出されたオブジェクトをそこへ書き出すメソッド。

  • print(PrintWriter)
  • println(PrintWriter)

の2つがあります

new File('log.txt').withPrintWriter{ out ->
    'Hello, GDK world!\n'.print(out)
    'Hello, GDK world!\'.println(out)
}

これを実行すると、log.txt ファイルに以下のように書き込まれます:

Hello, GDK world!
Hello, GDK world!

println() は改行を勝手に書き出してくれますよっと。

sprintf() メソッド

sprintf() メソッドは printf() メソッドと同じくフォーマットを指定しますが、標準出力へ書き出す代わりにフォーマットされた文字列を返します。

assert sprintf('I am %s.', 'waman') == 'I am waman.'
assert sprintf('I am %s, %d years old.', 'waman', 100) == 'I am waman, 100 years old.'

複数のプレースホルダーを使う場合は複数のオブジェクトを引数に渡す必要がありますが、printf() と同じく配列やリストにする必要はないようで。

toString(), inspect(), dump() メソッド

最後はオブジェクトの情報を文字列として返すメソッド。

  • toString()
  • inspect()
  • dump()

の3つがあります。 toString() は Java にもありますが、配列やコレクションの toString() メソッドは GDK でオーバーライドされてるようで、もっと意味ありげな文字列が返されます。 他の2つは GDK のドキュメントには説明が軽く書かれてますが、実際にいくつかのオブジェクトで試してみるのが一番かと。 ということでサンプルを:

def printStrings(arg){
    println "toString() : $arg"
    println "inspect()  : ${arg.inspect()}"
    println "dump()     : ${arg.dump()}"
    println()
}

//***** Integer *****
printStrings(100)
// toString() : 100
// inspect()  : 100
// dump()     : <java.lang.Integer@64 value=100>

//***** BigDecimal *****
printStrings(100.0)
// toString() : 100.0
// inspect()  : 100.0
// dump()     : <java.math.BigDecimal@7919 intVal=null scale=1 precision=4 stringCache=100.0 intCompact=1000>

//***** String *****
printStrings('倭マン')
// toString() : 倭マン
// inspect()  : '倭マン'
// dump()     : <java.lang.String@13314c2 value=倭マン hash=20124866 hash32=0>

//***** List *****
printIStrings([0, 1, 2, 3])
// toString() : [0, 1, 2, 3]
// inspect()  : [0, 1, 2, 3]
// dump()     : <java.util.ArrayList@e1b83 elementData=[0, 1, 2, 3] size=4 modCount=1>

//***** Set *****
printStrings([0, 1, 2, 3] as Set)
// toString() : [0, 1, 2, 3]
// inspect()  : [0, 1, 2, 3]
// dump()     : <java.util.LinkedHashSet@6 map=[0:java.lang.Object@826e7, 1:java.lang.Object@826e7, 2:java.lang.Object@826e7, 3:java.lang.Object@826e7]>

//***** Map *****
printStrings([a:0, b:1, c:2, d:3])
// toString() : [a:0, b:1, c:2, d:3]
// inspect()  : ['a':0, 'b':1, 'c':2, 'd':3]
// dump()     : <java.util.LinkedHashMap@18c header=null=null accessOrder=false table=[null, null, c=2, d=3] size=4 threshold=3 loadFactor=0.75 modCount=4 hashSeed=1408156617 entrySet=[a=0, b=1, c=2, d=3] keySet=null values=null>

//***** Array *****
printStrings([0, 1, 2, 3] as Object[])
// toString() : [0, 1, 2, 3]
// inspect()  : [0, 1, 2, 3]
// dump()     : <[Ljava.lang.Object;@a3d150>

//***** User-defined Class *****
class Person{
    String name
    int age
}

def waman = new Person(name:'倭マン', age:100)
printStrings(waman)
// toString() : Person@8f2016
// inspect()  : Person@8f2016
// dump()     : <Person@8f2016 name=倭マン age=100>

inspect() が返す文字列は toString() のものとそんなに変わらないけど、dump() は結構内部情報をさらけ出してるようですね。 プロパティですらない private なフィールドっぽいものも出力されてるよね・・・ まぁ、こんな感じで。

次回はメタプログラミング用メソッドの予定。

追記

println(Object) と println(PrintWriter) の GDK のドキュメントをみると

/**
 * Print a value formatted Groovy style to self if it is a Writer,
 * otherwise to the standard output stream.
 */
public void print(Object value);

/**
 * Print to a console in interactive format.
 */
public void print(PrintWriter out);

となってるんですが、英語力不足のためかいまいち何を言ってるのか・・・ とりあえず

def log = new File('log.txt')
log.withWriter{ writer ->
    writer.print('Hello, GDK world!')
}
assert log.text == 'Hello, GDK world!'

という風に writer オブジェクトに対して println(Object) を呼び出すと、標準出力ではなくその Writer へ引数のオブジェクトの文字列表現が書き出されるようです。 println(Object) も同じです。 print(PrintWriter) に関しては「interactive format」というのがよくわからん・・・

プログラミングGROOVY

プログラミングGROOVY