Edited at

Kotlinのdata classに配列を入れるとtoString()に配列の中身が全部表示されて困る話

More than 1 year has passed since last update.

Kotlinのdata classはとても便利だけど,微妙にかゆいところに手が届かないときもあるね,という話.


TL;DR (長い! 3行で!)


  • data classにbyte配列を格納したい

  • でもtoString()に配列の中身が全部表示されて,logcat1行文のバッファにすら入り切らん

  • 配列を格納するだけの別classを作って入れ子にしてやるといい感じ


はじめに

Kotlinのdata classで撮影した写真のJPEG Bufferと保存先File名やMeta Dataなど一式を格納するようなコンテナクラス↓を作って,Kotlinのdata classはラクでいいなぁと思ってました.


PhotoData.kt

data class PhotoData(val filepath: String, val jpeg: ByteArray, val meta: String) {

}

しかし,保存ThreadでPhotoData#toString()を使ってLog出力をしたところ,

default_log.png

のように,byteひとつひとつがIntに変換されて,全要素が表示されるようになっていて,配列の終端かlogcatの1行文のバッファがなくなるかまで表示されてしまいます.

JPEGデータが同一なのか別モノなのか,が知りたいだけなのに,さすがにコレでは困るのでなんとかしよう,というお話です.


やりたいこと

data class にbyte配列などを格納したときでも,配列全部を表示するのでなく,配列が同一のものなのか別モノなのか,が分かる程度にtoString()でヨロシクlog出力したい.


Kotlinのdata classがJavaにどう変換されているか?

というわけで,data classで自動生成されているtoString()がJAVA変換後にどういうCodeになっているかを見てみます.

↓ のサイトを参考に,KotlinのCodeをJAVAに変換してみます.

http://tech.actindi.net/2016/12/15/kotlin.html


PhotoData.kt

data class PhotoData(val filepath: String, val jpegBuffer: ByteArray) {

}

このdata class内に自動生成されるtoString()のCodeはJAVAに変換後,↓ のようになります.


PhotoData.java

public final class PhotoData

{
public String toString()
{
return (new StringBuilder())
.append("PhotoData(fileFullPath=")
.append(fileFullPath)
.append(", jpegBuffer=")
.append(Arrays.toString(jpegBuffer))
.append(")")
.toString();
}
}

このなかの

Arrays.toString(jpegBuffer)

が配列全出しの要因のようですね.

Array#toString()の仕様を調べると,↓ のようになっていて,byteひとつひとつをStringBuilder使って文字列に詰めています.


Arrays.java

public static String toString(byte[] array) {

if (array == null) {
return "null";
}
if (array.length == 0) {
return "[]";
}
StringBuilder sb = new StringBuilder(array.length * 6);
sb.append('[');
sb.append(array[0]);
for (int i = 1; i < array.length; i++) {
sb.append(", ");
sb.append(array[i]);
}
sb.append(']');
return sb.toString();
}

byte配列かArraysか何かに細工して挙動変えられないかなーと思ってたのですが,無理そうですね.

(じつはできるぜ! みたいなのあったら知りたいです!)


ByteArrayを格納するためだけのclassを作る

byte配列を素のままdata classに格納するとArrays#toString()が使われてしまうので,いっそ配列だけを格納するclassを別に作って入れ子にして,そのclassのtoString()をoverrideしてやれば,byte配列がそのまま出力されることを回避できるはず.

↓ こんな感じ


PhotoData.kt

class ByteBuffer(val buffer: ByteArray) {

override fun toString(): String {
return "${buffer.hashCode()}"
}
}

data class PhotoData(val fileFullPath: String, val jpeg: ByteBuffer) {
}


この構成で実際にlog catに出力させてみると,

modified.png

PhotoData(

fileFullPath=/storage/emulated/0/2017-07-29_151933.JPG,
jpeg=164835622
)

ってな感じの出力になって,同じ配列かどうか判定ができる程度のいい感じのlog catになっています.


まとめ

data classに長い配列を入れた時でも,log catが荒らされることが無いようにすることができました.

Kotlinのdata classはとても便利なのにArrayを入れたときのtoString()の標準挙動だけは残念な感じですね.

他にもっといい方法がありそうなきがするなーと思いつつ.

---///