Magnolia Tech

いつもコードのことばかり考えている人のために。

Scalaでネストしたcase classのインスタンスを作るときに気をつけること

きっかけはJson4sの、このissue.

github.com

テストクラスの中で定義したcase classが正しく判定されない、というもの。

いくらJson4sのコードを追いかけてもよく分からなかったところ、いつも色々なことを教えて頂くKenji Yoshidaさん(@xuwei_k)に教えて頂きました。

更に!普通にJson4sのREADMEにも書かれていました…最初から読めよっていう…

github.com

For classes defined in a trait it's a bit difficult to get to their companion object, which is needed to provide default values. We could punt on those but that brings us to the next problem, that the compiler generates an extra field in the constructor of such case classes. The first field in the constructor of those case classes is called $outer and is of type of the defining trait. So somehow we need to get an instance of that object, naively we could scan all classes and collect the ones that are implementing the trait, but when there are more than one: which one to take?

ネストしたcase classのコンストラクタの場合、Scalaコンパイラが$outerというフィールド名にouter classを入れておいてくれる! Json4sは全てのフィールドのインスタンスを作ってから、case classのインスタンスを作るので、この際にouter classが何も引数を取らないクラスであれば生成できる、というわけです(引数が必要なクラスだと生成に失敗します)。

Json4sでは、そのようなパターンにも対応できるようにouter classのインスタンスを渡す方法が用意されています。詳しくは先ほどのREADMEを参照して下さい。


この、Scalaではinner classのインスタンスがouter classのインスタンスに依存するのは、Scalaの経路依存型、という機能の話になり、以下のサイトが参考になりました。

53ningen.com

ネストしたクラスのインスタンス化について、JavaScalaで大きく違うところですね。