Kotlin / Programming

ザクっと理解するKotlin 抽象クラスとインターフェースの違い

Kotlinにおける抽象クラスとインターフェースは、オブジェクト指向プログラミングにおいて重要な役割を果たしています。

しかし、抽象クラスとインターフェースの違いや、それぞれがどのような場面で使われるのかを混同してしまうことも少なくありません。

そこで、このサイトではKotlinにおける抽象クラスとインターフェースの共通点と相違点について、実際のコード例を交えながら詳しく解説しています。

Kotlin 抽象クラスとインターフェースの違い

共通点

クラスに対するルールや規則を定義するために使用する

抽象クラスではこのようにルールを設定し、クラスで実装します。

class Class01: Abstract01() {
    override var property02 = "Class01 property01"
    
    override fun function01() {
        println("Class01 function01")
    }
}

abstract class Abstract01 {
    var property01 = "Abstract01 property01"
    abstract var property02: String
    
    abstract fun function01()
    fun function02() {
        println("Abstract01 function02")
    }
}

こちらはインターフェースでのルール設定とクラスでの実装です。

class Class02: Interface01 {
    override var property01 = "Class02 property01"
    val property03 = "Class02 property03"
    
    override fun function02() {
        println("Class02 function02")
    }
}

interface Interface01 {
    abstract var property01: String
    val property02: String
    	get() = "Interface01 property02"
    
    fun function01() {
        println("Interface01 function01")
    }
    
    fun function02()
}

詳しくは相違点とともに見ていきます。


相違点1

バッキングフィールドの有無

抽象クラスのプロパティにはバッキングフィールドがあります。

バッキングフィールドとはプロパティの値を保持するメモリ領域のことです。

つまり、プロパティは値を読み取るのも書き込むのもバッキングフィールドに対して行っています。

抽象クラスはバッキングフィールを持っているため、プロパティの宣言と同時に値を代入する(初期化する)ことができます。

abstract class Abstract01 {
    var property01 = "Abstract01 property01"
}

しかしインターフェースはバッキングフィールドがないため初期化できません。

interface Interface01 {
    var property01: String
    // var property01 = "Interface01 property01" // これはエラーになります
}

※ただし、下のようにバッキングフィールドを使わないデフォルト実装は使用できます。

interface Interface01 {
    var property01: String
    	get() = "Interface01 property01"
    	set(value) {
            "Interface01 property01"
        }
}

私はデフォルト実装の使い道が思いつきません。


相違点2

プロパティのオーバーライド

抽象クラスには大きく3タイプのプロパティがあります。

・初期化済みのプロパティ(オーバーライド不可)

abstract class Abstract01 {
    var property01 = "Abstract01 property01"
}

・抽象プロパティ(オーバーライド必須)

abstract class Abstract01 {
    abstract var property02: String
}

・初期化済みopenプロパティ(オーバーライド可)

abstract class Abstract01 {
    open var property03 = "Abstract01 property03"
}

考え方としては、抽象プロパティ以外の2つはopenクラスのプロパティのopen有無と同じです。

openがついているプロパティはオーバーライド可能ですが必須ではありません。

一方、抽象プロパティはオーバーライドが必須となります。


インターフェースには大きく2タイプのプロパティがあります。

・ボディなしプロパティ(オーバーライド必須)

interface Interface01 {
    var property01: String
}

・ボディありプロパティ(オーバーライド可)

interface Interface01 {
    var property02: String
    	get() = "Interface01 property02"
    	set(value) {}
}

こちらは両方ともオーバーライド可能です。

ボディがない方はこれだけでは何をしたいのかわからないためオーバーライド必須になります。

ボディがある方は任意となっています。

相違点3

関数のオーバーライド

抽象クラスには大きく3タイプの関数があります。

・ボディあり(オーバーライド不可)

abstract class Abstract01 {
    fun function01() {
        println("Abstract01 function01")
    }
}

・抽象関数(オーバーライド必須)

abstract class Abstract01 {
    abstract fun function02()
}

・open関数(オーバーライド可)

abstract class Abstract01 {
    open fun function03() {
        println("Abstract01 function03")
    }
}

プロパティと同様に、抽象関数以外はopenがあればオーバーライド可能になります。(必須ではありません)

抽象関数は、これだけでは何がしたいのか分からないためオーバーライド必須です。


インターフェースには大きく2タイプの関数があります。

・ボディなし関数(オーバーライド必須)

interface Interface01 {
    fun function01()
}

・ボディあり関数(オーバーライド可)

interface Interface01 {
    fun function02() {
        println("Interface01 function02")
    }
}

プロパティ同様に、ボディがない方は骨組みしかない状態なのでオーバーライド必須です。

ボディがある方はオーバーライドは可能ですが、必須ではありません。

参考文献

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です