Kotlinのグローバル変数!使い方と応用のたった12つの方法

Kotlinのグローバル変数のイラストと12のサンプルコードのテキストKotlin
この記事は約19分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

プログラムを学んでいると、変数の存在は無視できないものとなっています。

特に、Kotlinでのグローバル変数は、アプリケーション全体で共有される情報を格納するための非常に便利なツールです。

しかし、使い方を間違えると思わぬトラブルの原因にもなり得ます。

この記事を読めば、Kotlinのグローバル変数の使い方と応用を理解することができるようになります。

それでは、Kotlinとそのグローバル変数の基本について見ていきましょう。

●Kotlinとグローバル変数の基本

○Kotlinの特徴

Kotlinは、Javaよりもシンプルで直感的な文法を持ち、Androidアプリ開発などで急速に普及しているプログラム言語の一つです。

Null安全な言語設計や拡張関数などの独自の特徴を持ち、開発者から高く評価されています。

特に、Javaとの相互運用性も持っており、Javaのライブラリやフレームワークともスムーズに連携が可能です。

○グローバル変数とは?

グローバル変数は、プログラムのどこからでもアクセスできる変数のことを指します。

これは、特定の関数やクラスの外部に定義され、アプリケーション全体で共有される情報を保持する場合に非常に便利です。

しかし、誤った使用方法や管理がなされないと、意図しない変更や参照が行われる可能性もあるため注意が必要です。

●Kotlinでのグローバル変数の使い方

Kotlinでのグローバル変数は、その名の通りプログラム全体で使用可能な変数です。

しかし、その使い方や活用方法は、初心者には少々難しく感じるかもしれません。

ここでは、基本的な使い方から応用まで、具体的なサンプルコードとともに紹介していきます。

○サンプルコード1:基本的なグローバル変数の宣言と使用

このコードでは、Kotlinでのシンプルなグローバル変数の宣言と使用方法を表しています。

// グローバル変数の宣言
var globalVar: Int = 10

fun main() {
    println(globalVar) // 10
    globalVar += 5
    println(globalVar) // 15
}

このコードを実行すると、最初は10と表示され、その後に5を加算した結果の15と表示されます。

globalVarはmain関数の外で宣言されているため、main関数内からもアクセスすることができます。

○サンプルコード2:関数内でのグローバル変数の利用

こちらのコードでは、関数内でグローバル変数をどのように利用するのかを表しています。

// グローバル変数の宣言
var count: Int = 0

fun increaseCount() {
    count += 1
}

fun main() {
    println(count) // 0
    increaseCount()
    println(count) // 1
}

このコードを実行すると、最初に0と表示され、関数increaseCountを呼び出した後に1と表示されます。

関数内でグローバル変数countが更新され、その結果がmain関数内で反映されています。

○サンプルコード3:グローバル変数の更新と参照

グローバル変数は、更新や参照が自由ですが、その利用方法に注意が必要です。

このコードでは、他の関数からグローバル変数を更新し、その結果を参照する方法を表しています。

// グローバル変数の宣言
var message: String = "Hello"

fun updateMessage(newMessage: String) {
    message = newMessage
}

fun displayMessage() {
    println(message)
}

fun main() {
    displayMessage() // Hello
    updateMessage("Goodbye")
    displayMessage() // Goodbye
}

このコードを実行すると、最初に”Hello”と表示され、メッセージを更新した後に”Goodbye”と表示されます。

関数updateMessageでグローバル変数messageの内容が更新され、その変更が関数displayMessageでも反映されています。

●Kotlinのグローバル変数の応用例

Kotlinのグローバル変数は基本的な使い方だけでなく、多彩な応用例も存在します。

コードの効率を上げるため、また、より洗練されたプログラムを書くための応用例について、サンプルコードを交えて解説していきます。

○サンプルコード4:複数のファイルでのグローバル変数の共有

このコード例では、複数のファイル間でグローバル変数を共有する方法を表しています。

// File1.kt
var sharedVar: Int = 0

// File2.kt
fun accessSharedVar() {
    println(sharedVar)  // グローバル変数を参照
    sharedVar += 5
}

fun main() {
    println(sharedVar)  // 0
    accessSharedVar()  
    println(sharedVar)  // 5
}

コメントを参考にすると、sharedVarFile1.ktで宣言され、File2.ktからアクセスされています。

このように、一つのグローバル変数を複数のファイルで共有することが可能です。

実行結果からも分かるように、accessSharedVar関数がグローバル変数を更新しているのが確認できます。

○サンプルコード5:オブジェクトとしてのグローバル変数

Kotlinでは、オブジェクト宣言を用いてシングルトンなオブジェクトを生成することができます。

このオブジェクトも一種のグローバル変数として働きます。

// オブジェクトの宣言
object MySingleton {
    var name: String = "Default"
    fun display() {
        println(name)
    }
}

fun main() {
    MySingleton.display()  // Default
    MySingleton.name = "Updated"
    MySingleton.display()  // Updated
}

このコードでは、MySingletonオブジェクト内にnameという変数とdisplayというメソッドが含まれています。

このnameは、オブジェクト外から直接アクセスと更新が可能で、グローバル変数の性質を持っています。

結果として、nameの値が更新され、更新後の値が表示されます。

○サンプルコード6:グローバル変数を使用したシングルトンパターン

シングルトンパターンは、一つのインスタンスのみを生成し、それを再利用するデザインパターンです。

Kotlinでは、グローバル変数の特性を活かして、簡単にシングルトンパターンを実装することができます。

object SingletonInstance {
    init {
        println("シングルトンインスタンスが生成されました。")
    }

    var data: String = "初期データ"
}

fun main() {
    val firstReference = SingletonInstance
    val secondReference = SingletonInstance

    println(firstReference.data)
    println(secondReference.data)

    // 更新
    firstReference.data = "更新データ"
    println(secondReference.data) 
}

このコードでは、objectキーワードを使用してシングルトンのインスタンスSingletonInstanceを生成しています。

このインスタンスはアプリケーション内で一度しか生成されないため、初期化のメッセージも一度しか表示されません。

main関数内で、同じシングルトンインスタンスに対して2つの参照を作成しても、そのデータは同じものとなります。

このため、firstReferenceでデータを更新すると、secondReferenceからもその更新を確認できます。

○サンプルコード7:デリゲートプロパティとグローバル変数

Kotlinはデリゲートプロパティという機能を提供しています。

これにより、プロパティのゲッターやセッターの動作を他のオブジェクトに委譲することができます。

グローバル変数と組み合わせることで、特定の操作時に何らかの処理を自動的に行うような動作を実現することができます。

var globalData: String by Delegate()

class Delegate {
    private var data: String = "初期データ"

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("getValueが呼び出されました。")
        return data
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("setValueが呼び出されました。")
        data = value
    }
}

fun main() {
    println(globalData)
    globalData = "新しいデータ"
    println(globalData)
}

このコードでは、グローバル変数globalDataDelegateクラスのインスタンスによってデリゲートされています。

そのため、globalDataへのアクセスや更新が行われると、DelegateクラスのgetValuesetValueが呼び出されます。

これにより、グローバル変数のアクセスや更新の際に、特定の動作を自動的に実行することができます。

上記の例では、データの取得や更新時にメッセージが表示されるようになっています。

○サンプルコード8:Lazy初期化とグローバル変数

Lazy初期化とは、変数が初めてアクセスされるまでその初期化を遅延させる技術です。

これにより、必要ない場合にはリソースを浪費せず、必要になったタイミングで初期化を行うことができます。

Kotlinでは、lazyデリゲートを利用して、この機能を簡単に実装することができます。

// グローバル変数のLazy初期化
val lazyInitializedGlobal: String by lazy {
    println("初期化を行います。")
    "Lazy Initialized Data"
}

fun main() {
    println("main関数の開始")
    println(lazyInitializedGlobal) // この時点で初期化が行われる
    println(lazyInitializedGlobal) // 既に初期化されているので、再度初期化は行われない
}

上記のコードで、グローバル変数lazyInitializedGloballazyを使って遅延初期化されています。

この変数に初めてアクセスされる際に、ブロック内の処理が実行され、その結果が変数に格納されます。

二度目以降のアクセスでは、既に格納された結果が使用され、ブロック内の処理は再度実行されません。

○サンプルコード9:コンパニオンオブジェクトとグローバル変数

Kotlinでは、Javaのstaticに似た機能として、コンパニオンオブジェクトがあります。

これを利用することで、クラスレベルでの変数や関数を定義することができ、グローバル変数と同様の役割を果たします。

class ExampleClass {
    companion object {
        // コンパニオンオブジェクト内の変数は、クラスレベルで共有される
        var sharedData: String = "初期データ"
    }
}

fun main() {
    println(ExampleClass.sharedData) // "初期データ" を表示
    ExampleClass.sharedData = "更新データ"
    println(ExampleClass.sharedData) // "更新データ" を表示
}

このコードでは、ExampleClass内のコンパニオンオブジェクトにsharedDataという変数が定義されています。

この変数は、クラスのインスタンスを生成しなくてもアクセスや更新が可能です。

○サンプルコード10:エクステンション関数とグローバル変数

Kotlinでは、既存のクラスに新しい関数を追加することなく、新しい関数を追加することができるエクステンション関数という機能があります。

このエクステンション関数を用いて、グローバル変数との相互作用を拡張することができます。

例えば、Stringクラスにグローバル変数の値を追加するエクステンション関数を作成してみましょう。

// グローバル変数の宣言
var globalString = "Global"

// Stringクラスにエクステンション関数を追加
fun String.appendGlobal(): String {
    // グローバル変数を追加して返却
    return this + globalString
}

fun main() {
    val localString = "Local"
    println(localString.appendGlobal())  // "LocalGlobal"と出力される
}

上記のコードでは、appendGlobalというエクステンション関数をStringクラスに追加しています。

この関数は、呼び出されたStringオブジェクトの末尾にグローバル変数globalStringの値を追加した新しいStringを返します。

○サンプルコード11:イベントリスナーとグローバル変数

イベントリスナーは、特定のイベントが発生したときに実行されるコードのブロックです。

これをグローバル変数と組み合わせることで、アプリ全体でのイベントの取得や処理が容易になります。

下記のサンプルコードでは、ボタンクリックイベントのリスナー内でグローバル変数を更新する例を表しています。

// グローバル変数の宣言
var clickCount = 0

fun main() {
    val button = Button()

    // ボタンのクリックイベントリスナーを設定
    button.setOnClickListener {
        // グローバル変数を更新
        clickCount++
        println("ボタンが$clickCount 回クリックされました。")
    }

    // クリックイベントをシミュレート
    button.click()  // "ボタンが1回クリックされました。"と出力される
    button.click()  // "ボタンが2回クリックされました。"と出力される
}

// シンプルなボタンクラスとリスナーの実装
class Button {
    private var listener: (() -> Unit)? = null

    fun setOnClickListener(l: () -> Unit) {
        listener = l
    }

    fun click() {
        listener?.invoke()
    }
}

上記のコードでは、Buttonクラスのインスタンスbuttonがクリックされるたびに、グローバル変数clickCountの値が増加します。

これにより、アプリケーション全体でボタンのクリック回数を追跡することができます。

○サンプルコード12:アノテーションとグローバル変数

Kotlinでは、特定の動作や情報をクラスや関数、プロパティに追加するためのアノテーションという機能が提供されています。

アノテーションを活用することで、グローバル変数の使用に関する情報や制約を明示的に表すことができます。

例として、あるグローバル変数が非常に重要であり、変更を避けるべきであることを表すアノテーションを考えてみましょう。

// 重要なグローバル変数であることを示すアノテーション
annotation class ImportantVariable

@ImportantVariable
var importantData = "重要なデータ"

fun main() {
    // このデータは重要なので、変更には注意が必要
    println(importantData)
}

このコードでは、ImportantVariableというアノテーションを定義し、importantDataというグローバル変数に適用しています。

このアノテーションが付与されている変数は、特別な処理や注意が必要であることを表す目的で使用されることを想定しています。

現在、このアノテーションは動作的な側面を持っていませんが、将来的には特定のツールやライブラリと組み合わせて、アノテーションが付与されたグローバル変数に対する処理やチェックを自動化することが考えられます。

例えば、開発ツールがこのアノテーションを検出し、変数の変更を検出した際に警告を表示するなどの拡張が考えられます。

このような利用方法を通じて、グローバル変数の安全な使用や管理をサポートすることが可能になります。

●グローバル変数の注意点と対処法

Kotlinにおいてグローバル変数は非常に便利な存在ですが、その使用には様々なリスクや注意点が伴います。

適切な知識と理解を持つことで、これらのリスクを最小限に抑えることができます。

ここでは、グローバル変数の使用に関する主な注意点と、それらを解決・回避するための対処法について解説します。

○スレッドセーフに関する注意

グローバル変数はアプリケーション全体でアクセス可能な変数です。

そのため、マルチスレッド環境下での同時アクセスによる競合や不整合が起こる可能性があります。

特に、複数のスレッドから同時に変数の読み書きが行われる場合、意図しない動作やデータの不整合が生じるリスクが高まります。

// グローバル変数の例
var counter = 0

fun increment() {
    for (i in 1..100) {
        counter++
    }
}

fun main() {
    // 二つのスレッドを生成し、並行にincrement関数を実行
    val thread1 = Thread { increment() }
    val thread2 = Thread { increment() }

    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()

    // counterの値を表示
    println(counter)  // 期待される値は200であるが、それより小さい値が表示されることがある
}

このコードはcounterというグローバル変数の値を2つのスレッドで並行に増加させるものです。

しかし、counter++はアトミック(不可分)な操作ではないため、2つのスレッドが同時にこの操作を行うと、counterの値が正しく増加しない可能性があります。

これを回避するための方法として、@Volatileアノテーションやsynchronizedブロックの使用が考えられます。

これらの手法を利用することで、グローバル変数への同時アクセスによる競合を防ぐことができます。

○メモリリークとの戦い

グローバル変数はアプリケーションのライフサイクル全体で生存し続けるため、不適切な使用や管理によりメモリリークの原因となる可能性があります。

特に、グローバル変数に大量のデータやコンテキストを持つオブジェクトを保持してしまうと、そのオブジェクトが不要になった後もメモリ上に残り続けるリスクが高まります。

メモリリークを防ぐための対策として、次のような手法が考えられます。

  1. グローバル変数には必要最小限のデータのみを保持する。
  2. グローバル変数に保持するオブジェクトのライフサイクルを明確に管理し、不要になったタイミングで適切にリリースする。
  3. WeakReferenceやSoftReferenceなどの弱参照を使用して、オブジェクトの参照を持つことで、ガーベジコレクションの対象となるようにする。

●グローバル変数のカスタマイズ方法

Kotlinのグローバル変数は非常に便利ながら、柔軟なカスタマイズが可能です。

ここでは、そのカスタマイズ方法の一部を詳細に紹介していきます。

○可視性修飾子を利用した制限

Kotlinでは、変数や関数の可視性を制限するためにprivate, protected, internalなどの修飾子を使用することができます。

これらを利用することで、グローバル変数のアクセス範囲を適切に制限することができます。

例えば、あるファイル内でのみアクセス可能なグローバル変数を宣言する場合は、private修飾子を利用します。

private var fileOnlyVariable = "この変数はこのファイル内でのみアクセス可能"

このコードでは、fileOnlyVariableというグローバル変数は、宣言されているファイル内でのみアクセス可能となっています。

他のファイルからは参照や変更ができません。

○カスタムゲッターとセッターの活用

Kotlinのプロパティは、カスタムのゲッターやセッターを持つことができます。

これを利用することで、グローバル変数の読み取りや書き込みの振る舞いをカスタマイズすることができます。

例えば、下記のコードは、グローバル変数に値が書き込まれる際の振る舞いをカスタマイズしています。

var customVariable: Int = 0
    set(value) {
        if (value >= 0) {
            field = value
        } else {
            println("負の値は設定できません。")
        }
    }

このコードでは、customVariableに負の値が設定されることを防ぐためのカスタムセッターを実装しています。

セッター内で、設定される値が0以上の場合のみ、実際に変数に値が設定されるようにしています。

このように、カスタムゲッターやセッターを利用することで、グローバル変数の振る舞いを細かく制御することができます。

まとめ

Kotlinのグローバル変数は、アプリケーション全体でアクセス可能な変数として、多くの場面で役立つツールとなっています。

この記事では、その基本的な使い方から応用例、さらにはカスタマイズ方法まで詳細に解説しました。

Kotlinのグローバル変数の特性や可視性修飾子によるアクセス制限、カスタムゲッターやセッターを利用した振る舞いのカスタマイズなど、多岐にわたるテクニックを紹介しました。

これらの知識を活用することで、Kotlinのグローバル変数をより効果的かつ安全に使用することが可能となります。

初心者から上級者まで、幅広い読者がこの記事から学び取ることができる内容となっておりますので、実際の開発に取り組む際にも参考にしていただければと思います。