読み込み中...

Kotlinのアクセス修飾子!初心者向け19選の使い方と詳細解説

Kotlinのアクセス修飾子の詳細解説とサンプルコード Kotlin
この記事は約26分で読めます。

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

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

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

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

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

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

はじめに

この記事を読めば、Kotlinのアクセス修飾子の使用方法やその詳細を習得することができるようになります。

アクセス修飾子とは何か、どのように使うのか、どのような場合に使うべきなのか、それに加えて、どのように活用するかについて、初心者目線でわかりやすく解説しています。

実用的な19のサンプルコードも紹介し、どのような場面でどの修飾子を使うべきかについても具体的に説明します。

●Kotlinのアクセス修飾子とは

Kotlinでコードを書く際に、変数やメソッド、クラスなどがどれだけ外部からアクセス可能かを制御するためのキーワードを「アクセス修飾子」と言います。

○アクセス修飾子の基本概念

Kotlinにおける主なアクセス修飾子は、publicprivateprotectedinternalの4つです。

それぞれがどのようなアクセスレベルを持つのかについて説明します。

  • public:この修飾子がついた要素は、どこからでもアクセス可能です。実は、Kotlinではこの修飾子がデフォルトです。つまり、明示的に修飾子を指定しない場合、publicとして扱われます。
  • private:private修飾子がついた要素は、宣言された同じファイル内か、同じクラス内からしかアクセスできません。
  • protected:protectedは、主にクラス内とそのサブクラス内からアクセスできるようにする修飾子です。ただし、トップレベルの宣言(クラス外部の変数や関数)には使えません。
  • internal:この修飾子がついた要素は、同じモジュール内からアクセス可能です。

●アクセス修飾子の使い方

アクセス修飾子の概念を押さえたところで、次に進んで実際の使い方を見ていきましょう。

それでは、各種のアクセス修飾子を具体的なサンプルコードとともに紹介します。

○サンプルコード1:public修飾子の使用例

Kotlinではpublicがデフォルトの修飾子であり、明示的に指定しなくても他のどの場所からでもアクセスできるようになっています。

// このクラスはpublicです。
public class PublicClass {
    // この関数もpublicです。
    public fun show() {
        println("Public function")
    }
}

このサンプルコードではPublicClassクラスとその中のshow関数がpublicと明示してあります。

この場合でも、publicを省略しても同じ意味になります。このクラスと関数はどこからでもアクセス可能です。

○サンプルコード2:private修飾子の使用例

private修飾子は、宣言された要素がその宣言がされているブロック内でのみアクセス可能であることを意味します。

class PrivateExample {
    // privateな変数です。
    private var privateVar: Int = 1

    // publicな関数からprivateな変数にアクセスします。
    fun showPrivateVar() {
        println("Private Var = $privateVar")
    }
}

fun main() {
    val example = PrivateExample()
    // このコードはエラーとなります。privateな変数なので。
    // println(example.privateVar)

    // これは動作します。
    example.showPrivateVar()
}

このコードで定義されたPrivateExampleクラスには、privateVarというprivateな変数があります。

この変数はクラス外から直接アクセスすることができません。

そのため、main関数内でexample.privateVarと書くとエラーになります。

ただし、クラス内のshowPrivateVar関数を使って値を表示することはできます。

これらのコードを実行すると、Private Var = 1という結果が得られます。

privateVarPrivateExampleクラス内でしかアクセスできないため、このような挙動となります。

○サンプルコード3:protected修飾子の使用例

次に紹介するprotected修飾子は、その名前が示す通り保護された範囲でのみアクセスを許可します。

具体的には、そのクラス自身とそのクラスを継承したサブクラスからアクセスが可能です。

外部のクラスからはアクセスすることはできません。

open class Parent {
    // protectedな変数
    protected var name: String = "Parent"

    fun showName() {
        println("Name in Parent: $name")
    }
}

class Child : Parent() {
    fun changeName(newName: String) {
        // 親クラスのprotected変数にアクセス
        name = newName
    }

    fun displayName() {
        println("Name in Child: $name")
    }
}

fun main() {
    val parent = Parent()
    parent.showName()  // 出力:Name in Parent: Parent

    val child = Child()
    child.displayName()  // 出力:Name in Child: Parent
    child.changeName("Changed")
    child.displayName()  // 出力:Name in Child: Changed
}

このサンプルコードにおいては、Parentクラスがprotectedな変数nameを持っており、Childクラスがそれを継承しています。

ChildクラスのメソッドであるchangeNameはこのname変数にアクセスして値を変更しています。

一方で、Parentクラス外部からはnameに直接アクセスできないため、値の変更はChildクラスを通して行います。

このコードを実行すると、最初にName in Parent: Parentと出力され、その後にChildクラスのnameが親クラスのnameと同じであることを確認できます。

さらに、changeName関数を用いて名前をChangedに変更後、Name in Child: Changedと出力されるのが確認できます。

このように、protectedな変数はサブクラスでも利用可能です。

○サンプルコード4:internal修飾子の使用例

Kotlinでは、モジュール内での可視性を制御するためのinternal修飾子も用意されています。

この修飾子は、同じモジュール内であればどこからでもアクセスできるようにします。

// internal修飾子が付いたクラス
internal class InternalClass {
    var id: Int = 1
}

fun main() {
    val internalObj = InternalClass()
    println("ID: ${internalObj.id}")  // 出力: ID: 1
}

ここで出てくるInternalClassは、internal修飾子によって同一モジュール内からはアクセス可能な状態になっています。

main関数内でInternalClassのインスタンスを作成し、そのidプロパティにアクセスしています。

このコードを実行すると、ID: 1と表示されます。

これはinternal修飾子によって、同じモジュール内からアクセスできるためです。

●アクセス修飾子の応用例

基本的な使い方に慣れたところで、アクセス修飾子の応用例について解説していきます。

独自のクラス設計やモジュール設計を行う際に、アクセス修飾子をどのように活用できるかを具体的なサンプルコードとともに紹介します。

○サンプルコード5:クラス外からのアクセス制限

通常、プロパティやメソッドが外部からアクセスできるかどうかを制限する場面があります。

下記の例では、BankAccountクラスにprivate修飾子を用いて、balanceプロパティへの直接アクセスを制限しています。

class BankAccount {
    private var balance: Int = 0

    fun deposit(amount: Int) {
        if (amount > 0) {
            balance += amount
        }
    }

    fun showBalance() {
        println("残高は$balance 円です。")
    }
}

fun main() {
    val account = BankAccount()
    account.deposit(5000)
    account.showBalance() // 残高は5000 円です。
    // account.balance = 10000 // コンパイルエラー
}

この例ではBankAccountクラス内でのみbalanceにアクセスできます。

depositメソッドを通じてのみ、balanceを操作できる設計になっています。

main関数でaccount.showBalance()を呼び出すと、”残高は5000 円です。”と出力されます。

一方で、account.balance = 10000のような直接的な操作はコンパイルエラーとなります。

○サンプルコード6:同じモジュール内からのアクセス

複数のクラスやファイルが同じモジュールに属している場合、その中で共有したいプロパティやメソッドが出てくることがあります。

そのような場合はinternal修飾子を使うと便利です。

// ファイル1
internal class SharedResource {
    var data: String = "Initial Data"
}

// ファイル2
fun main() {
    val shared = SharedResource()
    println("Data: ${shared.data}") // Data: Initial Data
    shared.data = "Updated Data"
    println("Data: ${shared.data}") // Data: Updated Data
}

こちらのサンプルコードでは、SharedResourceクラスがinternal修飾子によって同一モジュール内でアクセス可能な状態になっています。

main関数でSharedResourceのインスタンスを作成し、dataプロパティにアクセスしています。

このコードを実行すると最初に”Data: Initial Data”が出力され、その後に”Data: Updated Data”が出力されます。

○サンプルコード7:サブクラスからのアクセス

クラス階層において、親クラスのプロパティやメソッドをサブクラスで参照または操作する際に有用な修飾子がprotectedです。

この修飾子を使うと、親クラスのプロパティやメソッドがサブクラスからは参照できるが、それ以外のクラスからは参照できなくなります。

// 親クラス
open class Animal {
    protected var sound: String = "..."

    fun makeSound() {
        println("Animal makes $sound")
    }
}

// サブクラス
class Dog : Animal() {
    init {
        sound = "Woof" // 親クラスのprotectedプロパティにアクセス可能
    }

    fun dogSound() {
        println("Dog says $sound")
    }
}

fun main() {
    val myDog = Dog()
    myDog.makeSound() // Animal makes Woof
    myDog.dogSound()  // Dog says Woof
    // myDog.sound  // コンパイルエラー
}

このコードでは、親クラスAnimalprotectedで定義されたプロパティsoundがあります。

Dogクラス(サブクラス)では、このsoundプロパティに直接アクセスし、その値を”Woof”に変更しています。

main関数を実行すると、”Animal makes Woof”と”Dog says Woof”が順番に出力されます。

一方で、myDog.soundのようにmain関数から直接soundプロパティにアクセスしようとするとコンパイルエラーになります。

○サンプルコード8:同じファイル内からのアクセス

同じKotlinファイル内であれば、アクセス修飾子を省略(デフォルトはpublic)したり、privateを指定してもそのファイル内から自由にアクセスできます。

この特性は、小規模なプログラムやスクリプトで特に有用です。

class SampleClass {
    var visibleToEveryone = "I am Public"
    private var visibleInThisFile = "I am Private"
}

fun show() {
    val example = SampleClass()
    println("Public says: ${example.visibleToEveryone}")
    println("Private says: ${example.visibleInThisFile}")
}

fun main() {
    val example = SampleClass()
    println("Public says: ${example.visibleToEveryone}") // I am Public

    show() // Public says: I am Public, Private says: I am Private
}

この例ではSampleClassというクラスに二つのプロパティがあります。

一つは修飾子なしで定義されたvisibleToEveryone、もう一つはprivate修飾子で定義されたvisibleInThisFileです。

同じファイル内のshow関数からは、どちらのプロパティも問題なくアクセスできます。

main関数を実行すると、”Public says: I am Public”が出力された後に”Public says: I am Public, Private says: I am Private”が出力されます。

●アクセス修飾子を活用したコード設計

アクセス修飾子の基本的な使い方や応用例を学んだ後、今度はより高度なコード設計に焦点を当てます。

アクセス修飾子は、単に変数や関数に制限をかける以上のものです。

それをうまく使うことで、プログラム全体の安全性とメンテナンス性を高めることができます。

○サンプルコード9:モジュール間のデータのやり取り

モジュールとは、プログラムの一部分を独立させたものです。

internal修飾子は、同じモジュール内でのみアクセスを許可するため、モジュール間のデータのやり取りに役立ちます。

// モジュールA
internal class ModuleA {
    internal fun show() {
        println("This is ModuleA")
    }
}

// モジュールB
fun main() {
    val moduleA = ModuleA()
    moduleA.show()  // This is ModuleA
}

このコード例では、ModuleAというクラスがinternal修飾子で宣言されています。

このクラスは、同じモジュール内(この場合は同じファイル内)であれば自由に使うことができます。

main関数でもModuleAshowメソッドを呼び出し、”This is ModuleA”と出力されます。

○サンプルコード10:継承を考慮した設計

クラスの継承を行う場合、親クラスのプロパティやメソッドにどのようなアクセス修飾子を設定するかが重要です。

例えば、親クラスのメソッドをサブクラスでオーバーライドしたい場合、openキーワードを使ってそのメソッドをオーバーライド可能にします。

// 親クラス
open class Parent {
    open fun showMessage() {
        println("Message from Parent")
    }
}

// サブクラス
class Child : Parent() {
    override fun showMessage() {
        println("Message from Child")
    }
}

// 使用例
fun main() {
    val parent = Parent()
    val child = Child()

    parent.showMessage()  // Message from Parent
    child.showMessage()   // Message from Child
}

このコードでは、Parentクラスにopen修飾子が付いており、showMessageメソッドも同様にopenで宣言されています。

これによりChildクラスではshowMessageをオーバーライドできます。

main関数で各インスタンスのshowMessageを呼び出すと、それぞれのクラスに合わせたメッセージが出力されます。

○サンプルコード11:内部クラスのアクセス制限

Kotlinでは、クラス内にさらにクラスを定義することができます。

これを内部クラスと呼びます。内部クラスにもアクセス修飾子を指定することができ、それによって内部クラスのアクセス範囲を制限することが可能です。

class OuterClass {
    private inner class InnerClass {
        fun innerMethod() {
            println("This is inner method.")
        }
    }

    fun accessInner() {
        val inner = InnerClass()
        inner.innerMethod()
    }
}

fun main() {
    val outer = OuterClass()
    outer.accessInner()  // "This is inner method."
}

このサンプルコードでは、OuterClass内にInnerClassという内部クラスを定義しています。

そして、この内部クラスにprivate修飾子を指定しています。

このため、InnerClassOuterClassからしかアクセスできません。

accessInnerメソッドでは、InnerClassのインスタンスを作成してinnerMethodを呼び出しています。

このinnerMethodInnerClass内で定義されたメソッドで、”This is inner method.”と出力されます。

main関数でOuterClassのインスタンスを作成し、accessInnerメソッドを呼び出しています。

結果として、”This is inner method.”という出力が得られます。

○サンプルコード12:拡張関数とアクセス修飾子

Kotlinでは、既存のクラスに新しいメソッドを追加することができる拡張関数があります。

この拡張関数にもアクセス修飾子を適用することができます。

private fun String.addExclamation(): String {
    return "$this!"
}

fun main() {
    val message = "Hello"
    println(message.addExclamation())  // 出力は "Hello!"
}

このコードでは、StringクラスにaddExclamationという拡張関数を追加しています。

この拡張関数はprivate修飾子が指定されているため、同じファイル内からのみアクセス可能です。

main関数でこの拡張関数を呼び出すと、”Hello!”という出力が得られます。

●注意点と対処法

アクセス修飾子の使用にはいくつか注意すべきポイントがあります。

コーディングの際に問題を回避するために、これらの注意点とその対処法を理解しておくことが重要です。

○サンプルコード13:意図しないアクセスを避ける

アクセス修飾子を適切に設定しないと、予期せぬ場所からクラスやメソッドにアクセスされる可能性があります。

// 間違った設定の例
class DangerousClass {
    var sensitiveData: String = "Secret"
}

fun main() {
    val instance = DangerousClass()
    println(instance.sensitiveData)  // "Secret" が出力される
}

このコードのDangerousClassは、sensitiveDataという重要なデータを持っていますが、アクセス修飾子が設定されていないため、どこからでもアクセス可能です。

この問題を解決する一例として、private修飾子を使います。

// 修正後の安全な設定
class SafeClass {
    private var sensitiveData: String = "Secret"
}

このように修正すると、sensitiveDataにはSafeClass内からしかアクセスできなくなります。

○サンプルコード14:修飾子の組み合わせの注意点

Kotlinでは複数の修飾子を組み合わせることがありますが、その際には注意が必要です。

// コンパイルエラーになる例
private open class ContradictoryClass {
}

このコードではprivateopenが組み合わされていますが、これは矛盾しています。

openは継承を許可する修飾子なので、privateと併用することはできません。

○サンプルコード15:アクセス修飾子のオーバーライド

親クラスに設定されたアクセス修飾子は、子クラスでオーバーライドする際には維持されます。

しかし、それを変更することもできます。

open class Parent {
    protected open fun show() {
        println("This is parent class.")
    }
}

class Child : Parent() {
    public override fun show() {
        super.show()
        println("This is child class.")
    }
}

fun main() {
    val child = Child()
    child.show()
}

このコードでは、Parentクラスにprotectedshowメソッドが定義されています。

このshowメソッドは、Childクラスでpublicにオーバーライドされています。

main関数でChildクラスのインスタンスを作成し、showメソッドを呼び出すと、「This is parent class.」「This is child class.」と出力されます。

●カスタマイズ方法

Kotlinのアクセス修飾子は基本的なものから応用的な使い方まで多岐にわたりますが、それだけでなく、さらに高度なカスタマイズも可能です。

ここでは、そのような高度な利用法について詳しく説明します。

○サンプルコード16:カスタムアクセス修飾子の作成

Kotlinでは、直接アクセス修飾子をカスタムすることはできませんが、特定の動作をする関数を作って、それをアクセス制御に使用することはできます。

// カスタムアクセス制御を行う関数
fun customAccessControl(user: String): Boolean {
    return user == "admin"
}

class CustomAccessClass {
    fun restrictedFunction(user: String) {
        if (!customAccessControl(user)) {
            println("アクセス拒否")
            return
        }
        println("アクセス許可")
    }
}

fun main() {
    val myClass = CustomAccessClass()
    myClass.restrictedFunction("admin")  // "アクセス許可"が出力される
    myClass.restrictedFunction("guest")  // "アクセス拒否"が出力される
}

このコードで注目すべきはcustomAccessControl関数です。

この関数は引数userを受け取り、その値が”admin”であればtrueを、それ以外であればfalseを返します。

この関数を使ってCustomAccessClass内のrestrictedFunctionメソッドでアクセス制御を行っています。

main関数を実行すると、”admin”では「アクセス許可」、”guest”では「アクセス拒否」と表示され、カスタムアクセス制御が正しく動作していることがわかります。

○サンプルコード17:特定のクラスや関数にのみアクセスを許可する

Kotlinでは、特定のクラスや関数にだけアクセスを許可するような細かい制御も可能です。

そのためには、拡張関数や高階関数を用いる方法があります。

class SpecialClass {
    // 外部からはアクセスできないprivateメソッド
    private fun specialFunction() {
        println("特別な機能")
    }

    // 特定の関数にだけアクセスを許可する
    fun accessFrom(block: SpecialClass.() -> Unit) {
        block()
    }
}

fun main() {
    val obj = SpecialClass()

    obj.accessFrom {
        specialFunction() // "特別な機能" と出力される
    }
}

このコードではSpecialClassクラス内にspecialFunctionというprivateメソッドがあります。

これは通常、クラス外からは呼び出せません。

しかし、accessFromというメソッドを通じて、specialFunctionを呼び出すことができます。

このようにして、特定の関数やクラスからのみアクセスを許可することが可能です。

○サンプルコード18:ライブラリのアクセス制限のカスタマイズ

Kotlinを使っていると、外部ライブラリを多用するケースも多くあります。

外部ライブラリは便利な一方で、必要以上に多くの機能が公開されていることがあります。

これをカスタムアクセス修飾子と組み合わせて制御する方法を見ていきましょう。

// ライブラリクラスのシミュレーション
open class LibraryClass {
    open fun publicMethod() {
        println("Public method in library")
    }

    internal fun internalMethod() {
        println("Internal method in library")
    }

    private fun privateMethod() {
        println("Private method in library")
    }
}

// ラッパークラスでライブラリクラスを継承
class CustomLibraryClass : LibraryClass() {
    // ライブラリの公開メソッドをオーバーライド
    override fun publicMethod() {
        // 何らかの制御を行う
        super.publicMethod()
    }

    // 独自のメソッドを追加
    fun customMethod() {
        println("This is a custom method.")
    }
}

fun main() {
    val customLibrary = CustomLibraryClass()
    customLibrary.publicMethod()  // ライブラリの公開メソッドが呼び出される
    customLibrary.customMethod()  // 独自のメソッドが呼び出される
}

このコードでは、LibraryClassをシミュレーションしています。

このクラスにはpublicMethodinternalMethodprivateMethodの三つのメソッドがあります。

続いてCustomLibraryClassクラスでLibraryClassを継承しています。

ここではpublicMethodをオーバーライドしており、何らかの制御を行っています。

main関数を実行すると、publicMethodがオーバーライドされたものが呼び出され、「Public method in library」と出力されます。

続いてcustomMethodが呼び出され、「This is a custom method.」と出力されます。

○サンプルコード19:アノテーションとアクセス修飾子の組み合わせ

アクセス修飾子だけでなく、アノテーションを使用することで、さらに繊細なアクセス制御が可能です。

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
annotation class SpecialPermission

class AnnotationControl {
    @SpecialPermission
    fun specialFunction() {
        println("This function needs special permission.")
    }
}

fun executeFunctionWithPermission(instance: AnnotationControl, func: AnnotationControl.() -> Unit) {
    val annotation = instance::class.java.getMethod("specialFunction").getAnnotation(SpecialPermission::class.java)
    if (annotation != null) {
        instance.func()
    } else {
        println("Permission not granted.")
    }
}

fun main() {
    val controlInstance = AnnotationControl()

    executeFunctionWithPermission(controlInstance) {
        specialFunction()
    }
}

このコードでは、@SpecialPermissionというアノテーションを定義しています。

そしてAnnotationControlクラスのspecialFunctionメソッドにこのアノテーションを付与しています。

executeFunctionWithPermission関数では、メソッドに@SpecialPermissionアノテーションが付与されているかどうかをチェックしています。

main関数を実行すると、specialFunctionメソッドに@SpecialPermissionアノテーションが付与されているので、このメソッドが実行され、「This function needs special permission.」と出力されます。

まとめ

この記事でKotlinのアクセス修飾子について、初心者にもわかるように詳細に解説してきました。

Kotlinでは、これらの修飾子を使ってコードの安全性を高め、保守性を向上させることが可能です。

Kotlinのアクセス修飾子は、プログラミングスキル全体を向上させる手段の一つと言えるでしょう。

これからもKotlinの様々な機能を学んで、より効率的かつ安全なコードを書く力を身に付けていきましょう。

それでは、この記事の内容が皆さんのKotlinでのプログラミングライフに役立つことを心より願っています。