ProgrammingAndroid 開発者

Kotlinにおける抽象クラスとインターフェースの違いを説明してください。どのアプローチをいつ適用するか、言語における実装の特性や継承のニュアンスは何ですか?

Hintsage AIアシスタントで面接を突破

回答。

Kotlinでは、抽象クラスabstract class)とインターフェースinterface)は抽象的なロジックを宣言するために使用されますが、いくつかの重要な違いがあります。

  • 抽象クラスは状態(フィールド)、メソッドの実装、および抽象メソッドを含むことができます。クラスは1つの抽象(または通常の)クラスのみを継承できます。抽象クラスにはコンストラクタがあります。
  • インターフェースはプロパティの宣言(状態を持たない!)、デフォルトメソッドの実装、および抽象メソッドを含むことができます。Kotlin 1.1以降、インターフェースはプロパティの実装を含むことができますが、それらには状態を持たせることはできません。クラスは複数のインターフェースを実装できます。
  • インターフェースは状態(バックフィールド)を持つことができないのに対し、抽象クラスは持つことができます。

コード例

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では不可能です。この問題はアプリケーションの拡張段階で明らかになりました。クラスを急遽再編成する必要があったため、かなりの技術的負債が発生しました。