Scalaでコンパイルを行うと「ambiguous reference to overloaded definition」と出力される

Scalaのクラスでコンストラクタのオーバーロードを行おうとコードを書いてコンパイルしたらエラーが出たのでメモります。
事象
** Scalaで書かれたソースコードをコンパイルすると以下のメッセージが出力されコンパイルエラーになる。**
ambiguous reference to overloaded definition
原因
2つの引数をとる関数をカリー化した際に、二番目の引数を省略するようなサブコンストラクタを定義した場合、二番目の引数がそもそも存在していない方のコンストラクタを呼び出しているのか、それともメインコンストラクタに対して部分適用を行なっているのかの判断がつかないため上記のようなエラーが発生します。
以下に例を載せます。
1 2 3 4 5 6 7 8 9 10 |
object ErrorSample { def main(args: Array[String]): Unit = new ErrorSample("テストだよ!") } class ErrorSample(val testString: String)(val testInt: Int) { println(s"testString: $testString") println(s"testInt: $testInt") def this(testString: String) = this(testString)(0) // <= Compile error. } |
この場合、何がいけないのかというと、
new ErrorSample("テストだよ!")
というのはカリー化されたコンストラクタにおいては
val f = new ErrorSample("テストだよ!") f(10)
のように部分適用されて、あとから残りの引数を適用し実行するのか、
val f = new ErrorSample("テストだよ!")
だけで、サブコンストラクタを呼び出しているものなのか、コンパイラには判断がつかないということです。
言われてみればこんなの人間にも判断できません。そのためエラーが出るのです(コンパイラはホント賢い)
対策
この場合はそもそもサブコンストラクタを作らないという方法と引数の順番を変更するという方法が有効かと思います。
サブコンストラクタを作らない場合
1 2 3 4 5 6 7 8 |
object ErrorSample { def main(args: Array[String]): Unit = new ErrorSample("テストだよ!")() } class ErrorSample(val testString: String)(val testInt: Int = 0) { println(s"testString: $testString") println(s"testInt: $testInt") } |
コンストラクタの引数の順番を変更する場合
1 2 3 4 5 6 7 8 9 10 |
object ErrorSample { def main(args: Array[String]): Unit = new ErrorSample("テストだよ!") } class ErrorSample(val testInt: Int = 0)(val testString: String) { println(s"testString: $testString") println(s"testInt: $testInt") def this(testString: String) = this(0)(testString) // <= Successful } |
以上を比較した場合、最初にやりたかったことをちゃんとできているのは後者の コンストラクタの引数の順番を変更する場合 だと思うので、KUJIRAはこの方法を使いました。
これについては色々と良し悪しがあると思うので状況に応じてどちらを使うかは判断していただければいいかと思います。
それでは解決したので今日はここまで。