Help us understand the problem. What is going on with this article?

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

More than 3 years have 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()の標準挙動だけは残念な感じですね.

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

---///

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away