エクステンション
既存のクラスの機能を拡張する基本的な方法は、そのクラスを継承して派生クラスを作成することです。
しかし、Kottlinでは継承以外にもクラスの機能を拡張する方法があります。
これを、「エクステンション」といいます。
「エクステンション」て拡張できるのは、クラスの関数とプロパティです。
「エクステンション」を追加するクラスのことを「レシーバ」と呼びます
関数のエクステンション
クラスに関数を追加するためには、次のような記述をします。
fun レシーバ.追加したい関数名(引数): 返り値の型 {
処理
}
それでは、サンプルを見てみましょう。
fun main(args: Array) {
val num = 5
println("${num}の階乗は、${num.Functional()}")
}
// 整数の階乗を求める関数をInt型にエクステンションとして追加する
fun Int.Functional(): Int {
// this はそのインスタンス自身をさす。
if (this > 1) {
return this * (this - 1).Functional()
} else {
return 1
}
}
上のサンプルのように、エクステンションの中で、
そのエクステンションを呼び出しているインスタンス自身にアクセスするには、 「this」を使います。
プロパティのエクステンション
プロパティのエクステンションも関数とエクステンションと同じように記述することができます。
val レシーバ.プロパティ名: 型
get() {
}
var レシーバ.プロパティ名: 型
get() {
}
set(value) {
}
それでは、サンプルを見てみましょう。
fun main(args: Array) {
val num = 5
println("${num}の階乗は、${num.Functional}")
}
val Int.Functional: Int
get() {
if (this > 1) {
return this * (this - 1).Functional
} else {
return 1
}
}
5の階乗は、120
エクステンションの解決
エクステンションは、あるクラスから「.」で呼び出すことのできる関数やプロパティを追加するだけで、
実際にそのクラスのメンバを追加するわけではありません。
そのことを確認するために、継承とエクステンションの動きの違いを次のサンプルで確認しましょう。
fun main(args: Array) {
val obj: Base = Derived()
// objの実体はDerivedなので、Derivedのメンバーが呼び出される
obj.PrintMember()
// objの型はBaseなので、Baseのエクステンションが呼び出される
obj.PrintExtension()
}
open class Base {
open fun PrintMember() = println("Member of Base is called.")
}
class Derived : Base() {
override fun PrintMember() = println("Member of Derived is called.")
}
fun Base.PrintExtension() = println("Extensino of Base is called.")
fun Derived.PrintExtension() = println("Extension of Derived is called.")
Member of Derived is called.
Extensino of Base is called.
エクステンションは、あくまでそのエクステンションを呼び出している変数の型によって決まります。
そのため、上のサンプルのようにその実体が派生クラスであっても、派生クラスのエクステンションが呼び出されることはありません。
nullableなレシーバ
レシーバがnullableの場合は、少し注意が必要です。
というのも、エクステンションの中で、thisを利用する場合、thisはもちろんnullableなので、
nullチェックのあとでないとメンバを呼び出すことができないからです。