ProgrammierungAndroid-Entwickler

Beschreiben Sie die Unterschiede zwischen abstrakten Klassen und Schnittstellen in Kotlin. Wann sollte welcher Ansatz verwendet werden, welche Implementierungsmerkmale gibt es in der Sprache und was sind die Besonderheiten der Vererbung?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

In Kotlin werden abstrakte Klassen (abstract class) und Schnittstellen (interface) verwendet, um abstrakte Logik zu deklarieren, aber es gibt wesentliche Unterschiede zwischen ihnen:

  • Abstrakte Klasse kann Zustände (Felder), Implementierungen von Methoden und abstrakte Methoden enthalten. Eine Klasse kann nur von einer abstrakten (oder normalen) Klasse erben. Abstrakte Klassen können Konstruktoren haben.
  • Schnittstelle kann Eigenschaftserklärungen (ohne Zustandsspeicherung!), Standardimplementierungen von Methoden und abstrakte Methoden enthalten. Ab Kotlin 1.1 kann eine Schnittstelle Implementierungen von Eigenschaften enthalten, aber diese dürfen keinen Zustand haben. Eine Klasse kann viele Schnittstellen implementieren.
  • Schnittstellen dürfen keinen Zustand (backing field) enthalten, während abstrakte Klassen dies können.

Beispielcode

abstract class Animal(val name: String) { abstract fun makeSound() fun info() = println("Ich bin $name") } interface Movable { fun move() } class Cat(name: String) : Animal(name), Movable { override fun makeSound() = println("Miau") override fun move() = println("Die Katze bewegt sich") }

Fangfrage.

Kann eine Schnittstelle in Kotlin Zustände (backing field) enthalten?

Die meisten antworten, dass man Eigenschaften in einer Schnittstelle deklarieren kann, aber sie haben dennoch kein backing field — nur Getter/Setter ohne Wertspeicherung. Selbst wenn man get() = ... schreibt — der Wert wird jedes Mal berechnet.

Beispiel:

interface MyInterface { val value: Int get() = 42 // keine Wertspeicherung, Berechnung zur Laufzeit }

Beispiele für reale Fehler aufgrund mangelnder Kenntnisse der Feinheiten des Themas.


Geschichte

In einem großen Projekt versuchte ein leitender Entwickler, den Zustand in einer Schnittstelle (zum Beispiel einen Zähler) zu speichern, in der Erwartung, dass die Eigenschaft var count: Int ein backing field hätte. Infolgedessen mussten alle Klassen, die die Schnittstelle implementierten, die Wertspeicherung unterschiedlich implementieren, was zu instabiler Logik führte (Daten gingen verloren, wenn vergessen wurde, den Setter zu überschreiben).


Geschichte

Ein Entwickler verwendete eine abstrakte Klasse anstelle von mehreren Implementierungen verschiedener Verhaltensstrategien (Strategiemuster). Infolgedessen gab es ein Problem: Nur eine Basisklasse pro Hierarchie, aber für verschiedene Verhaltensaspekte war eine Komposition erforderlich. Die Architektur musste auf Schnittstellen umgestellt werden.


Geschichte

Das Team versuchte, gleichzeitig von zwei abstrakten Klassen zu erben (eine mit Logik, die andere mit allgemeinen Utilities), was in Kotlin nicht möglich ist. Das Problem trat bereits in der Erweiterungsphase der Anwendung auf. Dies führte zu erheblichen technischen Schulden, da die Klassen dringend reorganisiert werden mussten.