Kotlinでは、抽象クラス(abstract class)とインターフェース(interface)は抽象的なロジックを宣言するために使用されますが、いくつかの重要な違いがあります。
abstract class Animal(val name: String) { abstract fun makeSound() fun info() = println("私は$nameです") } interface Movable { fun move() } class Cat(name: String) : Animal(name), Movable { override fun makeSound() = println("ニャー") override fun move() = println("猫が動く") }
Kotlinのインターフェースが状態(バックフィールド)を持つことができますか?
ほとんどの人はインターフェースでプロパティを宣言できると答えますが、それらにはバックフィールドはありません—値を保持せず、ゲッター/セッターのみです。たとえ get() = ... と書いても、値は毎回計算されます。
例:
interface MyInterface { val value: Int get() = 42 // 値を保持せず、その場で計算 }
物語
大規模なプロジェクトで、シニア開発者がインターフェースに状態を保持しようとしました(たとえば、カウンタ)。var count: Int プロパティがバックフィールドを持つと期待していたため、インターフェースを実装したすべてのクラスで値の保持を異なる方法で実装しなければならず、不安定なロジック(セッターをオーバーライドしないとデータが失われる)が発生しました。
物語
ある開発者が、異なる行動戦略の多重実装が必要な場面で抽象クラスを使用しました(戦略パターン)。その結果、基底クラスが1つだけの階層になり、行動のさまざまな側面に対してコンポジションが必要になりました。インターフェースにアーキテクチャを変更する必要がありました。
物語
チームは2つの抽象クラス(1つはロジック用、もう1つは一般的なユーティリティ用)を同時に継承しようとしましたが、これはKotlinでは不可能です。この問題はアプリケーションの拡張段階で明らかになりました。クラスを急遽再編成する必要があったため、かなりの技術的負債が発生しました。