拡張関数
CoddyのKotlinジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 30/57。
拡張関数は、型のソースコードを変更せずに、既存の型へ関数を追加します。その名前はレシーバー型の後に記述し、関数内ではthisがレシーバーを参照します。thisは省略することもできます。
fun String.shout() = uppercase() + "!"
fun Int.isEven() = this % 2 == 0mainの中では次のようにします。
println("hello".shout())
println(7.isEven())
println(listOf(1, 2, 3, 4).count { it.isEven() })出力:
HELLO!
false
2拡張は自分で作成したクラスにも使用でき、拡張プロパティによって計算プロパティを追加できます。拡張から保存された状態を追加することはできず、レシーバーの公開メンバーにのみアクセスできます。
class Temperature(val celsius: Double)
val Temperature.fahrenheit: Double
get() = celsius * 9 / 5 + 32
fun Temperature.describe() = "$celsius C is $fahrenheit F"main内で:
println(Temperature(100.0).describe())出力:
100.0 C is 212.0 F拡張は静的に解決されます。式の宣言された型によって、オブジェクトのクラスではなく、プログラムのコンパイル時に拡張が選択されます。これは、オーバーライドされたメンバーとの主な違いです。
open class Shape {
open fun member() = "shape member"
}
class Circle : Shape() {
override fun member() = "circle member"
}
fun Shape.ext() = "shape extension"
fun Circle.ext() = "circle extension"mainの内部では次のようになります。
val s: Shape = Circle()
println(s.member())
println(s.ext())出力:
circle member
shape extensionクラスに同じシグネチャを持つメンバーがすでに存在する場合は、常にメンバーが優先されます。型に属し、オーバーライドされる可能性がある振る舞いにはメンバーを使用します。自分が所有していないクラスの書式設定など、外部から追加するヘルパーには拡張を使用します。
class Robot {
fun greet() = "member greet"
}
fun Robot.greet() = "extension greet"
fun Robot.wave() = "extension wave"mainの内部で:
val r = Robot()
println(r.greet())
println(r.wave())出力:
member greet
extension waveチャレンジ
簡単Money(cents) は Money.kt に用意されています。MoneyExtensions.kt でその拡張を記述してください。数値を Money に変換する拡張プロパティ Int.dollars と Int.cents(12.dollars は 1200 セントになります)、$12.50(負の金額の場合は -$3.05)を返す Money.format()、合計を Money として返す List<Money>.total()、および金額を差が最大 1 セントになる parts 個の金額に分割し、大きいものを先に並べる Money.split(parts) を実装します。
用意されているコードは、12 dollars または 50 cents の行と、部数を示す数値を含む最後の行を読み取ります。そして各金額、total: $12.50、split 3: $4.17 $4.17 $4.16 を出力します。
コードは MoneyExtensions.kt と Money.kt に記述します。Main.kt には用意された入力/出力コードが含まれており、編集できません。
自分で試してみよう
fun main() {
// 提供された入出力コード: そのままにしておく
val input = generateSequence(::readLine).toList()
val amounts = input.dropLast(1).map { line ->
val (n, unit) = line.split(" ")
if (unit == "dollars") n.toInt().dollars else n.toInt().cents
}
for (m in amounts) println(m.format())
val total = amounts.total()
println("total: ${total.format()}")
val parts = input.last().toInt()
println("split $parts: " + total.split(parts).joinToString(" ") { it.format() })
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Kotlinプレイグラウンド