【Kotlin】レッスン5-6:静的メンバとインスタンスメンバの違いと使い方を学ぼう

ながみえ

一つ前のページではアクセス修飾子とカプセル化について学習しました。

今回は 静的メンバインスタンスメンバ について見ていきましょう。

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:石取りゲームを作ろう

<<前のページ

学習記事一覧

次のページ>>

静的メンバとインスタンスメンバの解説|違いと使い方を理解しよう

メンバとは、プロパティ(定数や変数)とメソッドを合わせて呼ぶときの総称です。

そして「静的メンバ」と「インスタンスメンバ」は、クラスの中で宣言されるメンバの分類を示し、それぞれ異なる用途を持ちます。

本記事ではこれらの違いと具体的な使い方について詳しく解説します。

静的メンバとは何か|クラス全体で共有するプロパティやメソッド

静的メンバ とはクラス全体に属するプロパティやメソッドのことです。

静的メンバはクラスそのものに結びついており、インスタンス生成をしなくても直接利用することができます。

静的メンバを定義するには companion object{}キーワード を使用します。

class クラス名 {
    companion object {		// 静的メンバの定義
        var 変数名 = 値		 // 静的変数の宣言
        val 定数名 = 値		 // 静的定数の宣言
        fun メソッド名() {	// 静的メソッドの定義
            // このメソッドの処理
        }
    }
}
fun main(){
    println(クラス名.定数名) // インスタンス化せずに定数を参照
    クラス名.メソッド名()	// インスタンス化せずにメソッドを呼び出し
}
  • インスタンス生成せずに定数/変数やメソッドを呼び出せる。
  • インスタンスから呼び出すことも可能。クラス全体で共有されるため、どのインスタンスからアクセスしても同じ値となる。
  • 計算処理や設定値を保持する用途でよく使用される。

インスタンスメンバとは何か|オブジェクトごとに持つプロパティやメソッド

インスタンスメンバ とは各インスタンスに属する変数やメソッドのことです。

このサイトのLesson5-5までの記事内で使用してきたメンバは全てインスタンスメンバになります。

インスタンスメンバはインスタンスごとに独立して存在するため、同じクラスを元に作られた複数のオブジェクト間で異なる値を持つことができます。

class クラス名 {
    var 変数名 = 値			// インスタンス変数
    val 定数名 = 値			// インスタンス定数
    fun メソッド名() {	   // インスタンスメソッド
        // このメソッドの処理
    }
}
fun main() {
    val obj1 = クラス名()	// インスタンス生成
    obj1.変数名 = 値1		// インスタンス変数の呼び出し
    val obj2 = クラス名()	// インスタンス生成
    obj2.変数名 = 値2		// インスタンス変数の呼び出し
    
    println(obj1.変数名)	// 出力:値1
    println(obj2.変数名)	// 出力:値2
    obj1.メソッド名()	  // インスタンスメソッドの呼び出し
}
  • プロパティやメソッドにアクセスするにはインスタンス生成が必要。
  • 各インスタンス固有の値や動作を管理する際に使われる。
  • クラスを元にした複数のオブジェクトが異なるデータを扱う際に便利。

静的メンバとインスタンスメンバの違い|コード例で確認

静的メンバとインスタンスメンバの主な違いは下表の通りです。

特徴静的メンバインスタンスメンバ
スコープクラス全体に共有各インスタンスに属する
アクセス方法クラス名を通じてアクセスインスタンスを通じてアクセス
用途共通の設定値やロジックを管理するためインスタンスごとに異なるデータや振る舞いを扱うため

両方のメンバを使ったコードの例を見てみましょう。

以下のプログラムは、ユーザーの情報を管理するクラス(User)を定義し、ユーザーの名前を保持するとともに、全体で何人のユーザーインスタンスが生成されたかをカウントする機能を持っています。

class User {
    companion object {				// 静的メンバの定義
        var userCount: Int = 0		// 静的変数: 全体のユーザー数を保持
        fun displayUserCount() {	// 静的メソッド
            println("Total users: $userCount")
        }
    }
    init {							// initブロック
        userCount++					// インスタンス作成時に静的変数にアクセス
    }
    var name: String = ""			// インスタンス変数: 各ユーザーの名前
    fun displayUserInfo() {			// インスタンスメソッド
        println("User name: $name")
    }
}

fun main() {
    val user1 = User()				// インスタンス生成
    user1.name = "Alice"			// インスタンス変数に値を代入
    user1.displayUserInfo()			// インスタンスメソッドの呼び出し

    val user2 = User()				// インスタンス生成
    user2.name = "Bob"				// インスタンス変数に値を代入
    user2.displayUserInfo()			// インスタンスメソッドの呼び出し

    User.displayUserCount()			// 静的メソッドの呼び出し
}
  • 各ユーザーごとに「名前」を持つインスタンス変数(name
  • クラス全体で共有される静的変数(userCount)を使い、ユーザーが生成されるたびに自動的に人数をカウント
  • 各ユーザーの名前を表示するメソッド(displayUserInfo)と、全体のユーザー数を表示する静的メソッド(displayUserCount
  • main関数で2人分のユーザー(AliceとBob)を生成し、それぞれの名前を設定して表示し、最後に全体のユーザー数を表示

まとめ|Kotlinでのメンバの役割と使い分け

Kotlinのクラス設計では、「静的メンバ(companion object内で定義)」と「インスタンスメンバ(クラスのプロパティやメソッド)」の違いを正しく理解することが重要です。

静的メンバはクラス全体で共有される情報や機能をまとめるのに適しており、インスタンスメンバは各オブジェクトごとに異なるデータや動作を管理するために使われます。

どちらを使うべきか迷ったときは、「このデータや処理はクラス全体で一つだけあれば十分か?」「それともインスタンスごとに別々の値や挙動が必要か?」という観点で考えると良いでしょう。

Kotlinの静的メンバとインスタンスメンバを使い分けることで、より分かりやすく、拡張性の高いコードを書くことができるようになります。

実際の開発でも、適切なタイミングでこれらを使い分けられるよう、今回の内容をしっかり身につけておきましょう。

練習問題|静的メンバとインスタンスメンバを使ってユーザー情報を管理しよう

静的メンバとインスタンスメンバを利用したプログラムを作成し、オブジェクト指向の基本を学びましょう。

このプログラムではユーザー情報を管理するクラスを作成します。

それぞれのユーザーごとに名前と年齢を管理し、全体のユーザー数を追跡する仕組みを実装してください。

プログラムを通じて、以下を実現します:

  • 個別のユーザー情報を出力する
  • 全体のユーザー数を出力する

この問題の要件

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

  1. クラスUserを定義すること:クラスには以下のメンバを含む。
    • 静的メンバ:
      1. companion objectを用いて全体のユーザー数を追跡するtotalUsers変数を定義する。
      2. 静的メソッドshowTotalUsersを作成し、全体のユーザー数を出力する。
    • インスタンスメンバ:
      1. name(名前)とage(年齢)を格納する変数を定義する。
      2. インスタンスメソッドdisplayUserInfoを作成し、ユーザーの名前と年齢を出力する。
  2. セカンダリコンストラクタを用いて、ユーザーの名前と年齢を初期化すること:
    • 新しいユーザーを作成するたびにtotalUsersの値を1増やすこと。
  3. メイン関数で以下を実行すること:
    • Userクラスを使って2人のユーザー(例: 太郎、25歳、花子、30歳)を作成する。
    • 各ユーザーの情報をdisplayUserInfoで出力する。
    • クラスメソッドshowTotalUsersを使用して、全体のユーザー数を出力する。

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

名前: 太郎, 年齢: 25
名前: 花子, 年齢: 30
現在のユーザー総数: 2

この問題を解くヒント

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

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

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

1:Userクラスの定義
  □ companion objectキーワードを使用して静的メンバの定義
  □ □ 静的変数totalUsersを定義し、初期値を0に設定
  □ □ 静的メソッドshowTotalUsersを定義
  □ □ □ 静的変数totalUsersの値を含む文字列を出力
  □ □ companion objectブロックを終了
  □ インスタンス変数nameとageを定義
  □ セカンダリコンストラクタを定義し、nameとageを初期化
  □ □ インスタンス変数nameに引数nameを代入
  □ □ インスタンス変数ageに引数ageを代入
  □ □ 静的変数totalUsersをインクリメント
  □ セカンダリコンストラクタを終了
  □ インスタンスメソッドdisplayUserInfoを定義
  □ □ インスタンス変数nameとageを含む文字列を出力
  □ インスタンスメソッドdisplayUserInfoを終了
  □ Userクラスの定義を終了
2:main関数の定義
  □ インスタンスuser1を作成し、引数”太郎”と25を渡す
  □ インスタンスuser2を作成し、引数”花子”と30を渡す
  □ インスタンスuser1のdisplayUserInfoメソッドを呼び出し、情報を出力
  □ インスタンスuser2のdisplayUserInfoメソッドを呼び出し、情報を出力
  □ クラスメソッドshowTotalUsersを呼び出し、総ユーザー数を出力
  □ main関数の定義を終了

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

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

// ユーザー情報を管理するクラス
class User {

    // 静的メンバ(クラス全体で共通する変数)
    companion object {
        /*【穴埋め問題1】
        ここでユーザーの総数を記録する変数totalUsersを定義し、初期値を0に設定するコードを書いてください。
        */

        /*【穴埋め問題2】
        ここでユーザー総数を表示する静的メソッドshowTotalUsersを定義し、
        現在のユーザー総数を表示するコードを書いてください。
        */
    }

    // インスタンスメンバ(個々のインスタンスが持つ変数とメソッド)
    /*【穴埋め問題3】
    ここでユーザーの名前を格納する変数nameと、年齢を格納する変数ageを定義してください。
    */

    // セカンダリコンストラクタで名前と年齢を受け取る
    /*【穴埋め問題4】
    ここでセカンダリコンストラクタを定義し、引数nameとageを受け取って
    インスタンス変数に値を代入するコードを書いてください。
    また、クラス変数totalUsersをインクリメントするコードも記述してください。
    */

    // ユーザー情報を表示するインスタンスメソッド
    /*【穴埋め問題5】
    ここでインスタンスメソッドdisplayUserInfoを定義し、
    名前と年齢を表示するコードを書いてください。
    */
}

// メイン関数
fun main() {
    // 新しいユーザーを作成
    /*【穴埋め問題6】
    ここでUserクラスのインスタンスuser1とuser2を作成し、それぞれ引数"太郎", 25と"花子", 30を渡すコードを書いてください。
    */

    // 各ユーザーの情報を表示
    /*【穴埋め問題7】
    ここでuser1とuser2のdisplayUserInfoメソッドを呼び出し、それぞれのユーザー情報を表示するコードを書いてください。
    */

    // 総ユーザー数を表示(クラスメンバを使用)
    /*【穴埋め問題8】
    ここでshowTotalUsersメソッドを呼び出し、総ユーザー数を表示するコードを書いてください。
    */
}

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

この問題の解答と解説

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

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

Q
正解コード
// ユーザー情報を管理するクラス
class User {

    // 静的メンバ(クラス全体で共通する変数)
    companion object {
        var totalUsers: Int = 0 // ユーザーの総数を記録する
        fun showTotalUsers() { // 総数を表示するクラスメソッド
            println("現在のユーザー総数: $totalUsers")
        }
    }

    // インスタンスメンバ(個々のインスタンスが持つ変数とメソッド)
    var name: String // ユーザーの名前
    var age: Int // ユーザーの年齢

    // セカンダリコンストラクタで名前と年齢を受け取る
    constructor(name: String, age: Int) {
        this.name = name
        this.age = age
        totalUsers++ // 新しいユーザーが作成されるたびにカウントを増やす
    }

    // ユーザー情報を表示するインスタンスメソッド
    fun displayUserInfo() {
        println("名前: $name, 年齢: $age")
    }
}

// メイン関数
fun main() {
    // 新しいユーザーを作成
    val user1 = User("太郎", 25)
    val user2 = User("花子", 30)

    // 各ユーザーの情報を表示
    user1.displayUserInfo()
    user2.displayUserInfo()

    // 総ユーザー数を表示(静的メンバを使用)
    User.showTotalUsers()
}
Q
正解コードの解説

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

クラスと静的メンバの定義

class User {
    companion object {
        var totalUsers: Int = 0
        fun showTotalUsers() {
            println("現在のユーザー総数: $totalUsers")
        }
    }
}
  1. class User:Userというクラスを定義しています。このクラスはユーザー情報を管理します。
  2. 静的メンバ(companion object:
    • クラス全体で共有される変数や関数を定義する場所です。
    • totalUsers:全体のユーザー数を記録する変数。companion objectに含まれているため、全てのインスタンスで共有されます。
    • showTotalUsers:現在のユーザー総数を出力する関数です。User.showTotalUsers()のようにクラス名を使って呼び出します。

インスタンスメンバの定義

var name: String
var age: Int
fun displayUserInfo() {
    println("名前: $name, 年齢: $age")
}
  • 各インスタンスごとに異なる値を持つ変数や関数を定義します。
  • name:各ユーザーの名前を保存する変数。
  • age:各ユーザーの年齢を保存する変数。
  • displayUserInfo:各ユーザーの名前と年齢を出力する関数です。インスタンスに対して呼び出します(例: user1.displayUserInfo())。

セカンダリコンストラクタでインスタンスを初期化

constructor(name: String, age: Int) {
    this.name = name
    this.age = age
    totalUsers++
}
  1. constructor:
    • クラスのインスタンスを作成するときに必要な情報(名前と年齢)を受け取ります。
    • this.namethis.ageで、受け取った値をインスタンスの変数に代入しています。
  2. totalUsers++:新しいユーザーが作成されるたびに、クラス全体のユーザー数を増やします。

メイン関数でクラスを活用

fun main() {
    val user1 = User("太郎", 25)
    val user2 = User("花子", 30)
    user1.displayUserInfo()
    user2.displayUserInfo()
    User.showTotalUsers()
}
  1. インスタンス作成:
    • val user1 = User("太郎", 25)のように、Userクラスのインスタンスを作成します。この時にconstructorが呼び出され、インスタンス変数が初期化されます。
  2. メソッド呼び出し:
    • 各ユーザーの情報をdisplayUserInfoで表示します。
    • クラス全体の情報(総ユーザー数)をshowTotalUsersで表示します。
Q
サイト改善アンケート & ご指摘/ご質問

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

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

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

<<前のページ

学習記事一覧

次のページ>>

記事URLをコピーしました