DEV Community

kaede
kaede

Posted on

Kotlin 基礎 Part 5 -- open クラスと abstract クラスと interface の違い

why

SOLID の依存性の逆転のために真ん中に依存させる何かが欲しい!
そのために作りたい機能のクラスを作るための 「継承/実装」元の 3 つを比較してみる。

  1. 通常のクラスをオープンにして関数の本体を書く
  2. 抽象クラスに抽象関数のガワだけを書く
  3. インターフェースに関数のガワだけを書く

通常のクラスをオープンにして関数の本体を書く

    open class Animal {
        open fun walk() {
            println("てくてく")
        }
        open fun eat() {
            println("もぐもぐ")
        }
    }
    class Human: Animal() {
        override fun walk() {
            println("二足歩行")
        }
    }

    val kaede = Human()
    kaede.walk()
    kaede.eat()
Enter fullscreen mode Exit fullscreen mode

二足歩行
もぐもぐ

Kotlin では open をつけたクラスは継承できる。
これを使って既に有るメソッドを上書きできる。

今回では Animal クラスを継承した Human クラスを使って
kaede というインスタンスを作成した。
上書きした walk を使うと 二足歩行が使える。
一方、上書きしてない eat も使える。

    val fromOpen = Animal()
Enter fullscreen mode Exit fullscreen mode

しかし、オープンクラス自体からもインスタンスをはやせてしまう。

    open class Animal {
        fun walk()
    }
Enter fullscreen mode Exit fullscreen mode

Function 'walk' without a body must be abstract

さらに、本体はいらないのに、関数の中身を必要とされてしまう。


抽象クラスに抽象関数のガワだけを書く

    abstract class Animal {
        abstract fun walk()
        open fun eat() {
            println("もぐもぐ")
        }
    }
Enter fullscreen mode Exit fullscreen mode

抽象にすれば、中身を書かなくても定義できる。
これのほうが実装と分離して書ける。

    val fromAbs = Animal()
Enter fullscreen mode Exit fullscreen mode

Cannot create an instance of an abstract class

抽象クラスからはインスタンスが作れなくなる。
これでオープンクラスのときのように元から作れる問題は解決

    class Human: Animal() {
        override fun walk() {
            TODO("Not yet implemented")
        }
    }
Enter fullscreen mode Exit fullscreen mode

継承先で override で作るべき関数がない場合は赤線がでて
誘導に従えば TODO 状態で作ってくれる。かなり書きやすい。

    abstract class Bisinessman {
        abstract fun earnMoney()
    }

    class Human: Animal(),Bisinessman() {
    }
Enter fullscreen mode Exit fullscreen mode

Only one class may appear in a supertype list

また、kaede が Animal の walk,eat だけでなく
Bisinessman の earnMoney も使いたくなった時
クラスなので複数の継承元を使うことはできなくて困る。


インターフェースに関数のガワだけを書く

上記の全ての問題はインターフェースを使うことで解決する。

interface Animal {
    fun walk()
    fun eat()
}
interface Businessman {
    fun earnMoney()
}
Enter fullscreen mode Exit fullscreen mode

これを使ってクラスを作って中身を作る時は

インターフェースの中身を「実装」する。
抽象クラスの中身を「継承」するのではなく。

欠点を強いていえば、何かのクラス内部に作ってしまうと

'Animal' is an interface so it cannot be local. Try to use anonymous object or abstract class instead

インターフェースは外に出せとエラーになるくらい。

interface Animal {
    fun walk() {
        println(
            "んぽ"
        )
    }
    fun eat()
}
Enter fullscreen mode Exit fullscreen mode

一方、上書きして実装しないときのデフォルトの挙動もインターフェースで書けてしまう。これをエラーにしたい時に何を使えばいいかは要検証。

Top comments (0)