演算子のオーバーロード: kotlin

演算子のオーバーロード

Kotlinでは作成する型に対して、事前定義済である演算子セットのオーバーロード実装を提供できます。これらの演算子は固定の記号表現(+や*など)と固定の優先順位を持っています。

演算子をオーバーロード実装するには、該当する型(二項演算時の左側の型や単項演算時の引数の型)に対して既定の名前を持つメンバ関数または拡張関数を用意します。

この演算子をオーバーロードする関数はoperator修飾子を付加する必要があります

※一文目から超訳を発動してしまった(ノ∀`)
"Kotlin allows us to provide implementations for a predefined set of operators on our types."の"our type"を、主語が"Kotlin"であり、我々(プログラマ側)の型と表現していることから"(プログラマが)作成する型"としたが、Kotlinが定義している全ての型で演算子オーバーロードが出来るなら"任意の型"とすべきか?

さらに、さまざまな演算子の演算子のオーバーロードを制限する規約について説明します。


単項演算

単項接頭演算子

対応する関数
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()

この表は、コンパイラがたとえば式 +a を処理する時に以下の手順を実行することを示しています。

- aの型を決定します。Tとします。

- operator修飾子があり、引数がないレシーバT用の関数 unaryPlus()(即ちメンバー関数または拡張関数)を検索します。

- 関数が存在しないかあいまいな場合は、コンパイルエラーになります。

- 関数が存在し、その戻り値の型がRである場合、式+aの型はRです。

これらの演算は他のすべての演算と同様に基本型用に最適化されており、その関数呼び出しのオーバーヘッドを発生させないことに留意してください。

一例として、単項マイナス演算子をオーバーロードする方法は以下の通りです。:

data class Point(val x: Int, val y: Int)
​
operator fun Point.unaryMinus() = Point(-x, -y)
​
val point = Point(10, 20)
​
fun main() {
   println(-point)  // "Point(x=-10, y=-20)"を出力します。
}

インクリメント及びデクリメント

※よくわからないが、"-"を2つ並べて記述すると1つしか表示されない?
wordpressかプラグインによってまとめられて罫線扱いになっている? 取り敢えず<code>タグで囲んだが…
----- ←ソースだと"-"x5

※wptexturizeの所為だった(ノ∀`) WordPressで一部の文字が勝手に置換されるのを無効化(123x456→123×456とか)

対応する関数
a++ a.inc() + 以下を参照
a-- a.dec() + 以下を参照

inc()およびdec()関数は値を返す必要があります。この値は++演算または--演算で使用された変数に割り当てられます。incまたはdecは自身を呼び出したオブジェクトを変更してはなりません。

コンパイラは後置形式の演算子(例えばa++)を処理するために以下の手順を実行します。:

- aの型を決定します。Tとします。

- 型Tのレシーバに適用可能な、operator修飾子があり、引数がない
関数inc()(即ちメンバー関数または拡張関数)を検索します。;

- 関数の戻り値の型がTのサブ型であることを判定します。

式を計算することによる作用は以下の通りです。

- aの初期値を一時保存の a0に格納します。

- a0.inc()の結果をaに割り当てます。

- 式の結果としてa0を返します。※後置インクリなのでaの初期値を返している。

a--の場合では処理は完全に類似しています

前置形式 ++aおよびa--の場合には処理は同じように機能し、作用は以下の通りです。:

- a.inc()の結果をaに割り当てます。

- 式の結果として、aの新しい値を返します。


二項演算

算術演算子

対応する関数
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.rem(b), a.mod(b) (非推奨)
a..b a.rangeTo(b)

この表の演算の場合、コンパイラーは「対応する関数」列の式を処理するだけです。

Kotlin 1.1以降、rem演算子がサポートされていることに注意してください。Kotlin 1.0は、Kotlin1.1で非推奨となったmod演算子を使用します。


以下は、指定された値で始まり、オーバーロードされた+演算子を使用してインクリメントできるCounterクラスの例です。:

data class Counter(val dayIndex: Int) {
    operator fun plus(increment: Int): Counter {
        return Counter(dayIndex + increment)
    }
}

'In' 演算子

対応する関数
a in b b.contains(a)
a !in b !b.contains(a)

inと!inの処理過程は同じですが、引数の順序が逆になっています。
※集団側を指す引数に対して関数定義をするってことを言いたいのかな?


インデックス付きアクセス演算子

対応する関数
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)

角括弧は、適切な数の引数を使用してget()およびset()の呼び出しに変換されます。


呼び出し演算子

対応する関数
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)

括弧は、適切な数の引数を使用してinvoke()の呼び出しに変換されます。


拡張された代入(複合代入演算子?)

対応する関数
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssign(b)
a %= b a.remAssign(b), a.modAssign(b) (非推奨)

複合代入演算の場合(例:a += b)、コンパイラは以下の手順を実行します。:

-右欄の関数が利用できる場合

-対応する二項関数(例えばplusAssign()であったらplus())も使用できる場合は、エラー(あいまいさ)を報告します。

ー戻り値の型がUnitであることを確認し、そうでない場合はエラーを報告します。

ーa.plusAssign(b);のコードを生成します。

それ以外の場合は、a = a + bのコードを生成しようとします。(これには型チェックが含まれます。a + bの型はaのサブ型である必要があります。)

注:Kotlinでは代入は式ではありません。


等価演算子および不等式演算子

対応する関数
a == b a?.equals(b) ?: (b === null)
a != b !(a?.equals(b) ?: (b === null))

これらの演算子は、関数 equals(other: Any?): Booleanでのみ機能します。これをオーバーライドしてカスタムの等価性判定の実装を提供できます。同じ名前の他の関数(equals(other: Foo)など)は呼び出されません。

注:===および!==(同一性判定)はオーバーロードできないため、これらの規則はありません。

==演算は特別です。nullを排除する複雑な式に変換されます。 null == nullは常にtrueであり、null以外のxのx == nullは常にfalseであり、x.equals()を呼び出しません。

screen 遮る、守る、篩い分ける、排除する


比較演算子

対応する関数
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

すべての比較は、Intを返す必要があるCompareTo()の呼び出しに変換されます。


プロパティ移譲演算子

ProvideDelegate、getValueおよびsetValue演算子関数は、委任されたプロパティで説明されています。


名前付き関数のInfix呼び出し

中置関数呼び出しを使用して、カスタムの中置演算を装うことができます。

※以下のように自作の関数を二項演算子として書けるようにできるらしい。

infix fun Int.shl(x: Int): Int { ... }
​
// infix記法を使用した関数呼び出しは、
1 shl 2
​
// 以下と同じです。
1.shl(2)

infix 接中辞、中置き型
infix operator 二項演算子
infix notation (omitting the dot and the parentheses for the call)
中置記法(呼び出しのドットと括弧を省略)