算譜王におれはなる!!!!

偏りはあると思うけど情報技術全般についてマイペースに書くよ。

Kotlinにおける演算子オーバロードと関数リテラル

Kotlinの関数についてのまとめ、その2。

※英語、または技術的な知識が至らず、内容に誤りが含まれるおそれがありますので、ご了承ください。また、載せているコード例は自作のものであり、参考サイトから引用したものではありません。
※本エントリの8割は参考サイトの翻訳です。残りの2割は私の解釈で加筆や変更を施した構成です。

以前書いたKotlinの関数に関する記事
Kotlinの関数について調べてみた - 算譜王におれはなる!!!!

演算子オーバロード

参考サイト http://confluence.jetbrains.net/display/Kotlin/Operator+overloading

Kotlinでは演算子オーバロードが可能です。その方法はすごくシンプルです。
Groovyのように演算子と、それに対応する関数シグネチャが準備されているというわけです。
定義済みの型(例えばIntやStringBuilderなど)の演算子オーバロードを行いたい場合は、拡張関数として定義します(拡張関数については1月9日の記事で紹介しています)。

fun main(args : Array<String>) {
  val str = StringBuilder("abcdefg")
  println(str[0, 3]) // abcd と表示される
  
  str[0, 3] = "ABCD"
  println(str) // ABCDefg と表示される
}

fun StringBuilder.get(start : Int, end : Int) : String? {
  return this.substring(start, end + 1)
}

fun StringBuilder.set(start : Int, end : Int, value : String) {
  this.replace(start, end + 1, value)
}

関数リテラル

参考サイト http://confluence.jetbrains.net/display/Kotlin/Function+literals

関数リテラルは、匿名関数です。

fun main(args : Array<String>) {
  val array = Array<String>(args.size, {i -> args[i]})
}

Arrayコンストラクタの第2引数に注目してください。そこには、配列の要素の初期化ルールを関数として渡します。上記の例では、その関数を関数リテラルとして直接渡しています。関数リテラルは中括弧( {} )で囲い、引数を冒頭に書き、関数内の処理と細い矢印(->)で区切ります。

上記のように、関数リテラルの取る引数が1つだけの場合に限り、次のように記述することもできます。

val array = Array<String>(args.size, {args[it]})

中括弧内の頭の引数を省略でき、引数の名前は it となります。これはGroovyと同じですね。

関数型

関数を引数として受け取るには、その型を指定する必要があります。Arrayクラスのコードを見てください。

class Array<T>(val size : Int, init : (Int) -> T) {
  //なんたらかんたら
}

第2引数(init : (Int) -> T)に注目してください。これはInt型を引数とし、T型を返す関数を意味し、名前はinitです。

関数リテラルの構文形式

関数リテラルを一切省略せずに書くと、次のようになります。

val less = {(a : Int, b : Int) : Boolean -> a < b}
  • 関数リテラルは常に中括弧に囲まれている
  • 完全な構文形式における引数の宣言は括弧の内部に書き、型アノテーション(任意)を付ける
  • 引数の後に、戻り値の型(任意)を置く
  • ボディは -> の後に書く

任意のアノテーションを除くと次のようになります[1]。

val less = {(a, b) -> a < b}

Kotlinでは括弧を除くことも許されています。すると、次のようなコードになります[1]。

val less = {a, b -> a < b}

[1]Kotlin Web Demoでこれらのコードを試すと、何故かコンパイルエラーとなります。コンパイラの不具合か、仕様の変更か…。

クロージャ

関数リテラル(だけでなく、ローカル関数やオブジェクト式)は、そのクロージャ、つまり外側のスコープで宣言された変数にアクセスできます。Javaとは異なり、クロージャ変数の値を変更することもできます

var x :Int = 2
val square = {x *= x}
square()
square()
square()
println(x) //256 が表示される
拡張関数リテラル

Kotlinでは拡張関数をサポートしており、拡張関数リテラルもサポートしています。

val square = { Int.() : Int -> this * this }
println(5.square()) //25が表示される