Interface: Wesen, Verträge in Java und Kotlin für Android

Autor: IT Sectr Veröffentlicht: 2026-02-18 Lesezeit: 9 Min.

Interface ist ein Vertrag, der eine Reihe abstrakter Methoden definiert, die eine Klasse implementieren muss. In Java und Kotlin sind Schnittstellen der primäre Mechanismus für Abstraktion und Polymorphismus. In Java 8+ können Schnittstellen default- und static-Methoden enthalten, in Kotlin — Standardimplementierungen. Laut Google Android Developers (2025) werden Schnittstellen in 90% der Android-Projekte zur Definition von Architekturschichten — Repositories, UseCases und Diensten — verwendet.

Wichtige Punkte

  • Interface ist ein abstrakter Typ, der Methodensignaturen ohne Implementierung definiert (vor Java 8)
  • Implements ist das Schlüsselwort, das eine Klasse mit einer Schnittstelle verbindet; eine Klasse kann mehrere Schnittstellen implementieren
  • Default method ist eine Methode mit Implementierung in einer Java-Schnittstelle, hinzugefügt in Java 8 für Abwärtskompatibilität
  • Kotlin interface unterstützt Eigenschaften mit Gettern und Methodenimplementierungen und ersetzt abstract class in vielen Szenarien
  • Markup interface ist eine leere Schnittstelle, die als Marker verwendet wird (Serializable, Cloneable, RandomAccess)

Was ist Interface in Java und Kotlin?

Interface ist ein Referenztyp, der abstrakte Methoden, Konstanten und Standardmethoden enthält. Eine Klasse, die eine Schnittstelle implementiert, muss Implementierungen für alle ihre abstrakten Methoden bereitstellen. In Java kann eine Schnittstelle keinen Zustand (Instanzfelder) haben; Kotlin hält sich ebenfalls an diese Einschränkung, unterstützt aber Eigenschaften mit Zugriffsmethoden.

Java
public interface Repository {
    T findById(Long id);
    List findAll();
    T save(T entity);
    void deleteById(Long id);
    
    default long count() {
        return findAll().size();
    }
}

@Entity
public class UserEntity {
    private Long id;
    private String email;
    // Getter und Setter
}

public class UserRepositoryImpl implements Repository {
    private final EntityManager em;
    
    public UserEntity findById(Long id) {
        return em.find(UserEntity.class, id);
    }
    
    public List findAll() {
        return em.createQuery("FROM UserEntity", UserEntity.class)
                .getResultList();
    }
    // andere Methoden
}

Repository<T> ist eine generische Schnittstelle mit CRUD-Methoden. Die default-Methode count() bietet eine Standardimplementierung, die überschrieben werden kann. UserRepositoryImpl implementiert die Schnittstelle mit EntityManager für den Datenzugriff. Dieser Ansatz ermöglicht das Testen der Datenschicht über einen Mock der Schnittstelle ohne eine echte Datenbank.

Interface vs abstract class: wann was wählen

Die Wahl zwischen einer Schnittstelle und einer abstrakten Klasse hängt vom Vorhandensein gemeinsamen Zustands und den Beziehungen zwischen Typen ab. Eine Schnittstelle definiert einen Vertrag (was eine Klasse tun kann), eine abstrakte Klasse definiert eine gemeinsame Implementierung (was eine Klasse ist).

KriteriumInterfaceAbstract Class
Zustand (Felder)Nur static final KonstantenJa, beliebige Felder
KonstruktorenNeinJa
MehrfachvererbungJa (implements)Nein (extends einer)
Zugriffsmodifikatorenpublic (Java 8-), default-MethodenAlle (private, protected, public)
Wann verwendenVertrag für nicht verwandte KlassenGemeinsame Basis für verwandte Klassen

In Clean Architecture werden Schnittstellen in der inneren Schicht (domain) platziert und Implementierungen in der äußeren Schicht (data). Dies ermöglicht die Einhaltung der Dependency Rule: Die äußere Schicht hängt von der inneren ab, aber nicht umgekehrt. Abstrakte Klassen werden häufiger für Vorlagenmethoden (Template Method Pattern) verwendet.

Interface in Kotlin: Eigenschaften und Delegaten

Kotlin-Schnittstellen sind flexibler als Java: Sie können abstrakte Eigenschaften deklarieren und Methodenimplementierungen bereitstellen. Anders als Java unterstützt Kotlin die Delegation über das Schlüsselwort by, was den Boilerplate-Code bei der Implementierung des Delegate-Musters erheblich reduziert.

Kotlin
interface ApiService {
    val baseUrl: String  // abstrakte Eigenschaft
    
    suspend fun fetchData(): Result>
    
    fun getEndpoint(path: String): String {
        return "$baseUrl/$path"  // Standardimplementierung
    }
}

class RetrofitApiService(
    override val baseUrl: String
) : ApiService {
    private val client = Retrofit.Builder()
        .baseUrl(baseUrl)
        .build()
    
    override suspend fun fetchData(): Result> {
        // Anfrageimplementierung
    }
}

// Delegation über by
interface Logger {
    fun log(message: String)
}

class ConsoleLogger : Logger {
    override fun log(message: String) = println(message)
}

class UserService(logger: Logger) : Logger by logger

UserService implementiert die Logger-Schnittstelle durch Delegation (by). Alle log()-Aufrufe werden an das logger-Objekt weitergeleitet, ohne Wrappermethoden zu schreiben. Dies ist ein Beispiel für Komposition, die in Java 5-10 Zeilen Boilerplate-Code erfordern würde.

Interface in Clean Architecture Android

Clean Architecture für Android trennt die Anwendung klar in Schichten. Schnittstellen fungieren als Grenzen zwischen Schichten: Domain definiert Repository- und UseCase-Schnittstellen, Data stellt Implementierungen bereit. Dies ermöglicht das Austauschen von Implementierungen ohne Änderung der Geschäftslogik — ein entscheidender Vorteil bei der Migration von Room zu Firebase oder von REST zu GraphQL.

Kotlin
// Domain layer — Schnittstelle (Vertrag)
interface UserRepository {
    suspend fun getUser(id: String): User
    suspend fun updateUser(user: User)
}

// Domain layer — use case (abhängt von der Schnittstelle)
class GetUserUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(id: String): Result {
        return runCatching { repository.getUser(id) }
    }
}

// Data layer — Implementierung
class UserRepositoryImpl(
    private val api: UserApi,
    private val dao: UserDao
) : UserRepository {
    override suspend fun getUser(id: String): User {
        val cached = dao.getUser(id)
        if (cached != null) return cached
        val remote = api.fetchUser(id)
        dao.insertUser(remote)
        return remote
    }
    // updateUser...
}

UserRepository ist eine Schnittstelle in der domain-Schicht. GetUserUseCase hängt von der Schnittstelle ab, nicht von der Implementierung. UserRepositoryImpl in der data-Schicht implementiert die Schnittstelle und kombiniert API und lokale Datenbank. Diese Architektur ermöglicht das Testen von UseCases mit einem Mock-Repository ohne Einrichtung einer Datenbank oder eines Netzwerks.

Default- und static-Methoden in Java 8+

Default-Methoden wurden in Java 8 für die evolutionäre Erweiterung von Schnittstellen ohne Bruch der Abwärtskompatibilität hinzugefügt. Wenn ArrayList die neue stream()-Methode, die zu Collection hinzugefügt wurde, nicht implementiert hätte, würde alter Code weiter funktionieren. Static-Methoden in Schnittstellen dienen als Hilfsmittel, die sich auf die Schnittstelle beziehen — eine Alternative zu Utility-Klassen.

Java
public interface Vehicle {
    void start();
    void stop();
    
    default void honk() {
        System.out.println("Beep!");
    }
    
    static Vehicle of(String type) {
        if ("car".equals(type)) return new Car();
        return new Bicycle();
    }
    
    // Konstante
    String CATEGORY = "transport";
}

Default-Methoden lösen das Diamond Problem: Wenn eine Klasse zwei Schnittstellen mit derselben default-Methode implementiert, verlangt der Compiler eine explizite Überschreibung. Static-Methoden werden über den Schnittstellennamen aufgerufen — Vehicle.of("car"), ohne Instanz.

Häufige Fehler beim Schnittstellendesign

Fehler im Schnittstellendesign führen zu fragwürdigem Code, Testkomplexität und SOLID-Verletzungen. Betrachten wir drei häufige Probleme.

Interface Pollution — zu viele Methoden in einer Schnittstelle

Eine Schnittstelle mit 15+ Methoden verstößt gegen das Interface Segregation Principle (ISP). Ein Beispiel ist das alte java.util.Dictionary mit 10+ Methoden. Lösung: Aufteilen in mehrere kleine Schnittstellen — ReadableRepository, WritableRepository, SearchableRepository. Ein Client (Dienst) hängt nur von den Methoden ab, die er benötigt.

Übermäßige Abstraktion — Schnittstelle für jede Klasse

Das Erstellen einer Schnittstelle für jede Klasse ohne echten Bedarf an Polymorphismus ist das Interface overkill-Antipattern. Indikator: Die Schnittstelle hat genau eine Implementierung und das Projekt hat keine Pläne, Alternativen hinzuzufügen. Lösung: Fügen Sie eine Schnittstelle nur hinzu, wenn eine zweite Implementierungsoption erscheint oder ein Mock für Tests benötigt wird.

Häufig gestellte Fragen

Wie unterscheidet sich interface von abstract class in Java?

Interface definiert nur einen Vertrag (Methodensignaturen), kann keinen Zustand haben und unterstützt Mehrfachvererbung. Abstract class kann Felder, Konstruktoren und implementierte Methoden enthalten, aber eine Klasse kann nur eine abstrakte Klasse erben. Seit Java 8 haben Schnittstellen default- und static-Methoden erhalten, was die Lücke verkleinert hat.

Kann eine Schnittstelle eine andere Schnittstelle erben?

Ja, in Java und Kotlin unterstützen Schnittstellen Vererbung. public interface AdvancedRepository<T> extends Repository<T>, Pageable ist eine Schnittstelle, die zwei andere kombiniert. Eine Klasse, die AdvancedRepository implementiert, muss alle Methoden beider Elternschnittstellen implementieren. Mehrfachvererbung ist nur für Schnittstellen erlaubt.

Was ist eine funktionale Schnittstelle in Java?

Funktionale Schnittstelle ist eine Schnittstelle mit einer einzigen abstrakten Methode (SAM — Single Abstract Method). Die Annotation @FunctionalInterface garantiert diese Einschränkung. Beispiele: Runnable, Callable, Comparator, Consumer. Funktionale Schnittstellen sind die Grundlage von Java 8-Lambda-Ausdrücken: () -> System.out.println() implementiert Runnable.

Warum werden default-Methoden in Java-Schnittstellen benötigt?

Default-Methoden ermöglichen das Hinzufügen neuer Methoden zu einer Schnittstelle, ohne alle implementierenden Klassen zu ändern. Beispielsweise hat Java 8 stream() zu Collection als default-Methode hinzugefügt. Ohne diesen Mechanismus hätten foreach(), stream() und andere Methoden Änderungen an Tausenden von Klassen im JDK erfordert. Default ist eine abwärtskompatible Erweiterungsmethode.

Wie löst Kotlin das Problem der Mehrfachvererbung?

Kotlin verbietet die Mehrfachvererbung von Klassen, erlaubt aber die Mehrfachimplementierung von Schnittstellen. Wenn zwei Schnittstellen eine Methode mit derselben Signatur und Standardimplementierung haben, verlangt der Compiler eine explizite Überschreibung mit dem Aufruf super<InterfaceName>.method(). Dies löst das Diamond-Problem auf Kompilierungsebene.

Zusammenfassung

  • Interface ist ein Vertrag, der Methoden definiert, die eine Klasse implementieren muss; der primäre Mechanismus für Polymorphismus in Java und Kotlin
  • Java 8+ fügte default- und static-Methoden zu Schnittstellen hinzu und verkleinerte die Lücke zu abstrakten Klassen
  • Kotlin interfaces unterstützen abstrakte Eigenschaften, Standardimplementierungen und Delegation über by
  • Clean Architecture verwendet Schnittstellen als Grenzen zwischen Domain- und Data-Schichten
  • Interface Segregation Principle erfordert die Aufteilung großer Schnittstellen in spezialisierte
  • Funktionale Schnittstellen (Single Abstract Method) sind die Grundlage von Lambda-Ausdrücken und Stream API
  • Empfehlung: Fügen Sie eine Schnittstelle hinzu, wenn eine zweite Implementierung erscheint oder ein Mock für Tests benötigt wird

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch