Kotlinで引数にジェネリクスを取る関数のオーバーロードを実装してみようと思ったときに詰まったのでメモ。
こんな感じで作ってみるとエラーが出る。
fun main() { class Moge { fun plusMoge(stringList: List<String>) { plusMoge(listOf(stringList.map { it.toInt() })) } fun plusMoge(intList: List<Int>) { intList.forEach { println(it + 1) } } } val moge = Moge() moge.plusMoge(listOf("1")) }
Platform declaration clash: The following declarations have the same JVM signature
とエラーが出てそもそもコンパイル出来ない。
こんなエラー。
Kotlin で関数のオーバーロードは型情報が消えるので出来ない
どうやらジェネリクスはコンパイル時に型情報が消去される仕様が起因していてオーバーロード出来ない。
Java ジェネリクスのポイント # コンパイル時に型情報は消去される / Qiita @pebblip
OKパターン
片方のジェネリクスを取る引数をなくす
無理やり実験。
片一方のplusMoge()
の引数をList<Int>
からInt
に変えてジェネリクスを取らない引数にしてみる。
そうすれば型情報がコンパイラに消されても当然呼び出し側から区別が付くのでコンパイル出来る。
普通の関数オーバーロード。
fun main() { class Moge { fun plusMoge(stringList: List<String>) { stringList.forEach { plusMoge(it.toInt()) } } fun plusMoge(int: Int) { println(int + 1) } } val moge = Moge() moge.plusMoge(listOf("1")) }
気になったので一応でコンパイルしてみる。
final class Moge { public final void plusMoge(@NotNull List stringList) { Intrinsics.checkNotNullParameter(stringList, "stringList"); Iterable $this$forEach$iv = (Iterable)stringList; int $i$f$forEach = false; Iterator var4 = $this$forEach$iv.iterator(); while(var4.hasNext()) { Object element$iv = var4.next(); String it = (String)element$iv; int var7 = false; boolean var9 = false; this.plusMoge(Integer.parseInt(it)); } } public final void plusMoge(int var1) { int var2 = var1 + 1; boolean var3 = false; System.out.println(var2); } public Moge() { } } Moge moge = new Moge(); moge.plusMoge(CollectionsKt.listOf("1"));
解決策
諦めて別の関数を作る
関数のオーバーロードは諦めて別々で関数に命名する。
潔くて一番早い。無理にオーバーロードしなくてもいいなら諦めた方が良さそう。
fun main() { class Moge { fun plusMogeForString(stringList: List<String>) { plusMoge(listOf(stringList.map { it.toInt() })) } fun plusMogeForInt(intList: List<Int>) { intList.forEach { println(it + 1) } } } val moge = Moge() moge.plusMogeForString(listOf("1")) moge.plusMogeForInt(listOf(1)) }
環境
Kotlin v1.5.21
コメント