【Kotlin】レッスン5-9:クラスの拡張とは?拡張関数とプロパティを使いこなそう

ながみえ

一つ前のページではメソッドのオーバーライドについて学習しました。

今回は クラスを拡張 について見ていきましょう。

Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:関数編
Lesson4:コレクション編
Lesson5:オブジェクト指向編

 ・Lesson5-1:クラスの基本を理解しよう
 ・Lesson5-2:プライマリコンストラクタを理解しよう
 ・Lesson5-3:セカンダリコンストラクタを理解しよう
 ・Lesson5-4:ふたつのコンストラクタを使いこなそう
 ・Lesson5-5:アクセス修飾子とカプセル化を理解しよう
 ・Lesson5-6:静的メンバとインスタンスメンバを理解しよう
 ・Lesson5-7:クラスの継承を理解しよう
 ・Lesson5-8:メソッドのオーバーライドを理解しよう
 ・Lesson5-9:クラスを拡張しよう ◁今回はココ
 ・Lesson5-10:抽象クラスを理解しよう
 ・Lesson5-11:インターフェースを理解しよう
 ・Lesson5-12:データクラスを理解しよう
 ・確認問題5-☆1:モンスター捕獲ゲームを作ろう
 ・確認問題5-☆2:マルバツゲームを作ろう
 ・確認問題5-☆3:石取りゲームを作ろう

<<前のページ

学習記事一覧

次のページ>>

クラスの拡張とは?|拡張関数とプロパティの基礎

クラスの拡張とは、既存のクラスやユーザーが定義したクラスに対して、新しいプロパティや関数を追加できる機能です。

この機能を使えばクラスのコードを変更することなく、独自の機能を簡単に追加できます。

初心者の方でも使いやすいこの機能は、標準クラス (StringInt など) や自分で作成したクラスを効率的に操作する際に非常に役立ちます。

本記事では「拡張プロパティ」と「拡張関数」の基本から具体例までを解説します。

クラス拡張の意味とメリット

ここまでの学習で使用してきたString型や Int型といったデータ型は、実はクラスの一種として実装されたものです。

つまり、文字列や数値を扱う機能がクラス内のメソッドやプロパティとして、あらかじめ提供されているわけです。

例えば、Stringクラスには文字列の長さを取得する lengthプロパティや、文字列をすべて大文字に変換する toUpperCase()関数などがあります。

しかしこうした既存のクラスに新しい機能を加えたい場合、コードを書き換えることはできません。

そこで役立つのが「クラスの拡張」です。拡張を利用すると、既存クラスにカスタムのプロパティや関数を “クラスの外から” 追加できます。

なお拡張に使用するプロパティや関数はトップレベルで定義する、すなわちメイン関数の外に書くことで、コード全体で使えるようになります。

拡張プロパティとは何か?|既存クラスを強化する方法

拡張プロパティとは既存のクラスに新しいプロパティを追加する仕組みです。

拡張プロパティを新たに定義するには以下のように書きます。

val クラス名.拡張プロパティ名: 型
	get() = 機能

たとえばStringクラスに「文字列の最初の文字を取得するプロパティ」を追加する例を見ていきましょう。

以下のコードでは String クラスにfirstCharという名前を付けた拡張プロパティを追加しています。

val String.firstChar: Char	// Stringクラスを拡張してfirstCharという名のプロパティを追加
    get() = this[0]			// firstCharプロパティの処理内容

fun main() {
    val text = "HelloKotlin"
    println(text.firstChar)	// 定数textにfirstCharプロパティをあてて出力: H
}

このコードを実行すると以下のように出力されます。

H

このように拡張プロパティを使えば、コードを簡潔にしつつ既存のクラスを拡張できます。

もう一つ例を見てみましょう。

以下はユーザーが定義した Userクラスに拡張プロパティを追加する例です。

class User(val name: String){}		// 空のUserクラスの定義

val User.isLong: Boolean			// Userクラスを拡張してisLongプロパティを追加
    get() = this.name.length > 10	// 文字列が10文字を超えるか判定

fun main() {
    val user = User("Alice")
    println(user.isLong)			// 定数userにisLongプロパティをあてて出力: false
}

この例ではUser クラスに isLong プロパティを追加しています。これにより、名前文字数が10文字以上か以下かを取得できるようになります。

拡張関数でメソッドを追加する具体的な書き方

拡張関数は既存のクラスに新しいメソッドを追加する仕組みです。

拡張関数を新たに定義するには以下のように書きます。

fun クラス名.関数名(引数: 型): 戻り値の型 {
    // 処理
    return 戻り値
}

たとえばStringクラスに、文字列の前に特定の文字列を付ける関数を追加する例を見てみましょう。

fun String.add(text: String): String {	// Stringクラスを拡張して、拡張関数addを定義
    return "$text$this"					// 戻り値(引数textと元の文字列を連結して返す)
}

fun main() {
    val text = "Kotlin"
    println(text.add("Hello, "))		// 定数textにadd関数をあてて出力: Hello, Kotlin
}

このコードを実行すると以下のように出力されます。

Hello, Kotlin

続いて、Userクラスに拡張関数を追加し、ユーザー毎に乱数のIDを生成するコードを見てみましょう。

class User{}		// 空のUserクラスの定義

fun User.generateId(): Int {		// Userクラスを拡張して、拡張関数generateIDを定義
    return (1000..9999).random()	// 1000~9999のランダムなIDを返す
}
fun main() {
    val user = User()				// インスタンス生成
    println(user.generateId())		// 出力例: 5273(呼ぶたびに異なる)
}

拡張関数を使用することで、このように既に作成済みのクラスを修正することなく、機能追加することが可能です。

まとめ|拡張機能を使いこなすためのポイントを整理しよう

Kotlinのクラス拡張は既存クラスやユーザー定義クラスに機能を追加する便利な方法です。

拡張プロパティや拡張関数は、コードを簡潔かつ直感的にするための強力なツールです。特に以下の点に注意してください:

  • トップレベルでの定義:クラスや関数の外で定義することで、広いスコープで利用可能になります。
  • 再利用性:拡張機能により、同じクラスを使う複数の箇所で同じ機能を効率的に再利用できます。
  • 注意点:既存クラスのプロパティやメソッドと名前が衝突しないように注意が必要です。

拡張の基本を理解したら、ぜひ自分で実装を試してみましょう!

練習問題|拡張プロパティと関数を使って独自の文字列処理を作ろう

文字列クラスを拡張し、プロパティと関数を追加する練習をしましょう。

まず文字列内の単語数を数える拡張プロパティを定義し、次に文字列内の単語順を反転する拡張関数を作成します。

これらを使い、サンプルの文字列に適用してその結果を画面に表示してください。

この問題の要件

以下の要件に従ってコードを完成させてください。

  1. 拡張プロパティ wordCount を作成し、文字列内の単語数を取得できるようにすること。
    • 空白で区切られた単語を数えること。
  2. 拡張関数 reverseWords を作成し、文字列内の単語順を逆に並べ替えること。
    • 単語間は空白で分割し、逆順にして結合すること。
  3. サンプル文字列として「Kotlin を 学ぶ のは 楽しい」を使用すること。
  4. 拡張プロパティ wordCount を使用して単語数を表示すること。
  5. 拡張関数 reverseWords を使用して単語を逆順に並べ替えた結果を表示すること。

ただし、以下のような実行結果となること。

「Kotlin を 学ぶ のは 楽しい」の単語数は: 5 個です。
「Kotlin を 学ぶ のは 楽しい」を単語順に反転すると: 楽しい のは 学ぶ を Kotlin

この問題を解くヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

Q
ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。

1:Stringクラスに拡張プロパティwordCountを定義
  □ getメソッドの定義
  □ □ 文字列を空白で分割し、空でない部分の数を返す
2:Stringクラスに拡張関数reverseWordsを定義
  □ 文字列を空白で分割し、順序を反転させて再結合した結果を返す
3:main関数の定義
  □ String型の変数sentenceに”「Kotlin を 学ぶ のは 楽しい」”を代入
  □ printlnを用いて、sentenceの単語数をwordCountプロパティで取得し出力
  □ printlnを用いて、sentenceの単語をreverseWords関数で反転して出力

Q
ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

// 文字列クラスに拡張プロパティを追加
/*【穴埋め問題1】
ここに文字列クラスに拡張プロパティwordCountを追加するコードを書いてください。
このプロパティは文字列内の単語数をカウントします。
*/

// 文字列クラスに拡張関数を追加
/*【穴埋め問題2】
ここに文字列クラスに拡張関数reverseWordsを追加するコードを書いてください。
この関数は文字列内の単語を反転した結果を返します。
*/

fun main() {
    // サンプル文字列
    val sentence = "Kotlin を 学ぶ のは 楽しい"

    // 拡張プロパティの使用例
    /*【穴埋め問題3】
    ここにsentenceを使ってwordCountプロパティを呼び出し、その結果を出力するコードを書いてください。
    */

    // 拡張関数の使用例
    /*【穴埋め問題4】
    ここにsentenceを使ってreverseWords関数を呼び出し、その結果を出力するコードを書いてください。
    */
}

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

この問題の解答と解説

この問題の正解コードとその解説は以下の通りです。

クリックして開いて確認してください。

Q
正解コード
// 文字列クラスに拡張プロパティを追加
val String.wordCount: Int
    get() {
        // 文字列内の単語数をカウント(空白で分割して数える)
        return this.split(" ").filter { it.isNotEmpty() }.size
    }

// 文字列クラスに拡張関数を追加
fun String.reverseWords(): String {
    // 文字列内の単語を反転する
    return this.split(" ").reversed().joinToString(" ")
}

fun main() {
    // サンプル文字列
    val sentence = "Kotlin を 学ぶ のは 楽しい"

    // 拡張プロパティの使用例
    println("「$sentence」の単語数は: ${sentence.wordCount} 個です。")

    // 拡張関数の使用例
    println("「$sentence」を単語順に反転すると: ${sentence.reverseWords()}")
}
Q
正解コードの解説

コードをブロックごとに分割して解説します。

拡張プロパティ wordCount

val String.wordCount: Int
    get() {
        return this.split(" ").filter { it.isNotEmpty() }.size
    }
  1. 拡張プロパティ:
    • 既存のクラス(この場合はString)に新しいプロパティを追加します。
    • wordCountは文字列内の単語数を数えます。
  2. このプロパティの仕組み:
    • split(" "): 空白で文字列を分割します。
    • filter { it.isNotEmpty() }: 空でない単語のみをフィルタします。
    • size: 単語の数を取得します。

: 文字列 "Kotlin を 学ぶ のは 楽しい" の単語数は5です。

拡張関数 reverseWords

fun String.reverseWords(): String {
    return this.split(" ").reversed().joinToString(" ")
}
  1. 拡張関数:
    • 既存のクラスに新しい関数を追加します。
    • reverseWordsは文字列内の単語順を反転します。
  2. この関数の仕組み:
    • split(" "): 空白で文字列を分割します。
    • reversed(): 単語のリストを逆順に並べ替えます。
    • joinToString(" "): 単語を空白で結合して新しい文字列を作ります。

: 文字列 "Kotlin を 学ぶ のは 楽しい" は反転すると "楽しい のは 学ぶ を Kotlin" になります。

メイン関数

fun main() {
    val sentence = "Kotlin を 学ぶ のは 楽しい"
    println("「$sentence」の単語数は: ${sentence.wordCount} 個です。")
    println("「$sentence」を単語順に反転すると: ${sentence.reverseWords()}")
}
  • 文字列sentenceを定義します。
  • 拡張プロパティwordCountを使用して単語数を表示します。
  • 拡張関数reverseWordsを使用して単語を反転した結果を表示します。
Q
サイト改善アンケート & ご指摘/ご質問

本サイトでは、みなさまの学習をよりサポートできるサービスを目指しております。
そのため、みなさまの「プログラミングを学習する理由」などを アンケート 形式でお伺いしています。

また記事の内容に関する ご指摘ご質問 もお待ちしています。

1.このサイトをどのように活用していますか?また、今後どのように活用したいですか?
5.気になっているサービス・商品があれば教えてください。
※ 特定の記事に関する内容の場合は、記事番号(レッスン〇-〇)をご記入願います。

<<前のページ

学習記事一覧

次のページ>>

記事URLをコピーしました