[kotlin] 演算子オーバーロードでちょっとハマったこと
とある事情から、kotlinで文字列の掛け算(演算子)を実装したくなりました。
kotlinには演算子オーバーロードの機能があるらしいのでそれを使おうとした時、ちょっとだけハマったので忘れないようにメモ。
環境
Kotlin version 1.3.41-release-150 (JRE 12+33)
やりたいこと
演算子オーバーロードで文字列の掛け算を実装する。
kotlinの演算子オーバーロード
演算子をオーバーロードするときはoperator
キーワードを使って以下のように実装する。
operator fun 関数名:戻り値{
中身
}
それぞれの演算子と関数名の対応は以下の表の通り
単項演算
演算子 | 関数 |
---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
! | a.not() |
インクリメント・デクリメント
演算子 | 関数 |
---|---|
a++ | a.inc() |
a– | a.dec() |
二項演算
演算子 | 関数 |
---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.mod(b) |
a..b | a.rangeTo(b) |
a in b | b.contains(a) |
a !in b | !b.contains(a) |
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.modAssign(b) |
添字演算
演算子 | 関数 |
---|---|
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i_1, …, i_n] | a.get(i_1, …, i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, …, i_n] = b | a.set(i_1, …, i_n, b) |
比較
演算子 | 関数 |
---|---|
a == b | a?.equals(b) ?: b === null |
a != b | !(a?.equals(b) ?: b === null) |
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
関数呼び出し
演算子 | 関数 |
---|---|
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i_1, …, i_n) | a.invoke(i_1, …, i_n) |
実装
kotlinのString
クラスはrepeat
メソッドを持っているらしいので、それをラップする形で拡張関数を実装
operator fun String.times(n:Int):String{
return this.repeat(n)
}
this
はなくてもいけるらしいけど、読みやすさのために書いておきます。
拡張関数
既存のクラスにメソッドを追加するかの様に関数を書けるkotlinの機能
使ってみます。
fun main() {
var abc = "abc "
println(abc * 3)
}
# abc abc abc
普通に掛け算を実装する分にはできました。
掛け算ができるなら複合代入演算*=
もできた方が自然です。
というわけで、作ろうと思い実装してみました。
operator fun String.timesAssign(n:Int):String{
return this.repeat(n)
}
が、コンパイルが通らない…
こんなエラーが出ました。
kotlinc MyString.kt -include-runtime -d bin/sample.jar
MyString.kt:19:7: error: assignment operators ambiguity:
public operator fun String.times(n: Int): String defined in root package in file MyString.kt
public operator fun String.timesAssign(n: Int): Unit defined in root package in file MyString.kt
a *= 10
^
演算子の解釈が曖昧…ひょっとして
先ほどの複合代入演算子の関数定義を消して複合代入演算を実行。
"a" *= 2
>>> "aa"
あ、出来た。
まとめ
- 演算対象の型と戻り値の型が同じ場合、複合代入演算は自動で作ってくれるっぽい。
おまけ
演算対象の型と戻り値の型が違っても、定義自体はされるっぽい。
operator fun String.times(n:Int):Int{
return this.length * n
}
>>> var a = "a"
>>> a * 2
res10: kotlin.Int = 2
>>> a *= 2
error: type mismatch: inferred type is Int but String was expected
a *= 2
^