Interface: essensen, kontrakt i Java och Kotlin för Android

Författare: IT Sectr Publicerad: 2026-02-18 Lästid: 9 min

Interface — är ett kontrakt som definierar en uppsättning abstrakta metoder som en klass måste implementera. I Java och Kotlin är gränssnitt den huvudsakliga mekanismen för abstraktion och polymorfism. I Java 8+ kan gränssnitt innehålla default och static metoder, i Kotlin — standardimplementeringar. Enligt Google Android Developers (2025) används gränssnitt i 90% av Android-projekt för att definiera arkitekturlager — repositories, UseCase och tjänster.

Huvudpunkter

  • Interface — abstrakt typ som definierar metodsignaturer utan implementering (före Java 8)
  • Implements — nyckelord som kopplar en klass till ett gränssnitt; en klass kan implementera flera gränssnitt
  • Default method — metod med implementering i Java-gränssnitt, tillagd i Java 8 för bakåtkompatibilitet
  • Kotlin interface stödjer egenskaper med getters och metodimplementeringar, och ersätter abstract class i många scenarier
  • Markup interface — tomt gränssnitt som används som markör (Serializable, Cloneable, RandomAccess)

Vad är Interface i Java och Kotlin?

Interface — är en referenstyp som innehåller abstrakta metoder, konstanter och standardmetoder. Klassen som implementerar ett gränssnitt måste tillhandahålla implementering av alla dess abstrakta metoder. I Java kan ett gränssnitt inte ha tillstånd (instansfält), Kotlin följer också denna begränsning men stödjer egenskaper med accessorer.

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;
    // getters och setters
}

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();
    }
    // övriga metoder
}

Repository<T> — ett generiskt gränssnitt med CRUD-metoder. Standardmetoden count() tillhandahåller en standardimplementering som kan överskridas. UserRepositoryImpl implementerar gränssnittet med EntityManager för dataåtkomst. Detta tillvägagångssätt möjliggör testning av datalagret via en mock av gränssnittet utan verklig databas.

Interface vs abstract class: när ska man välja vad

Valet mellan gränssnitt och abstrakt klass beror på förekomsten av gemensamt tillstånd och relationer mellan typer. Ett gränssnitt definierar ett kontrakt (vad klassen kan göra), en abstrakt klass — en gemensam implementering (vad klassen är).

KriteriumInterfaceAbstract Class
Tillstånd (fält)Endast static final konstanterJa, alla fält
KonstruktorerNejJa
Multiplelv nedärvningJa (implements)Nej (extends en)
Åtkomstmodifierarepublic (Java 8-), default metoderAlla (private, protected, public)
När ska man användaKontrakt för orelaterade klasserGemensam bas för relaterade klasser

I Clean Architecture placeras gränssnitt i det inre lagret (domain) och implementeringar i det yttre (data). Detta möjliggör efterlevnad av Beroenderegeln: det yttre lagret beror på det inre, inte tvärtom. Abstrakta klasser används oftare för mallmetoder (Template Method pattern).

Interface i Kotlin: egenskaper och delegater

Kotlin-gränssnitt är mer flexibla än Java: de kan deklarera abstrakta egenskaper och tillhandahålla metodimplementeringar. Till skillnad från Java stödjer Kotlin delegering via nyckelordet by, vilket avsevärt minskar boilerplate vid implementering av Delegate-mönstret.

Kotlin
interface ApiService {
    val baseUrl: String  // abstrakt egenskap
    
    suspend fun fetchData(): Result>
    
    fun getEndpoint(path: String): String {
        return "$baseUrl/$path"  // standardimplementering
    }
}

class RetrofitApiService(
    override val baseUrl: String
) : ApiService {
    private val client = Retrofit.Builder()
        .baseUrl(baseUrl)
        .build()
    
    override suspend fun fetchData(): Result> {
        // implementering av förfrågan
    }
}

// Delegering via 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 implementerar Logger-gränssnittet via delegering (by). Alla log()-anrop omdirigeras till loggerobjektet utan att skriva wrapper-metoder. Detta är ett exempel på komposition som i Java skulle kräva 5-10 rader boilerplate-kod.

Interface i Clean Architecture Android

Clean Architecture för Android delar tydligt upp applikationen i lager. Gränssnitt fungerar som gränser mellan lager: Domain definierar gränssnitt för repositories och UseCase, Data tillhandahåller implementeringar. Detta möjliggör utbyte av implementeringar utan att ändra affärslogiken — en viktig fördel vid migrering från Room till Firebase eller från REST till GraphQL.

Kotlin
// Domain-lager — gränssnitt (kontrakt)
interface UserRepository {
    suspend fun getUser(id: String): User
    suspend fun updateUser(user: User)
}

// Domain-lager — use case (beroende av gränssnitt)
class GetUserUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(id: String): Result {
        return runCatching { repository.getUser(id) }
    }
}

// Data-lager — implementering
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 — gränssnitt i domain-lagret. GetUserUseCase är beroende av gränssnittet, inte implementeringen. UserRepositoryImpl i data-lagret implementerar gränssnittet och kombinerar API och lokal databas. Denna arkitektur möjliggör testning av UseCase med ett mock-repository utan databas- eller nätverkskonfiguration.

Default och static metoder i Java 8+

Default-metoder lades till i Java 8 för evolutionär utvidgning av gränssnitt utan att bryta bakåtkompatibilitet. Om ArrayList inte hade implementerat den nya metoden stream() som lades till i Collection, skulle gammal kod fortsätta att fungera. Static-metoder i gränssnitt tjänar som verktyg relaterade till gränssnittet — ett alternativ till verktygsklasser.

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();
    }
    
    // konstant
    String CATEGORY = "transport";
}

Default-metoder löser diamond problem: om en klass implementerar två gränssnitt med samma default-metod, kräver kompilatorn explicit överskrivning. Static-metoder anropas via gränssnittsnamnet — Vehicle.of("car"), utan instans.

Vanliga misstag vid design av gränssnitt

Misstag vid design av gränssnitt leder till skör kod, svårigheter med testning och SOLID-överträdelser. Låt oss titta på tre vanliga problem.

Interface Pollution — för många metoder i ett gränssnitt

Ett gränssnitt med 15+ metoder bryter mot Interface Segregation Principle (ISP). Exempel — gamla java.util.Dictionary med 10+ metoder. Lösning: dela upp i flera små gränssnitt — ReadableRepository, WritableRepository, SearchableRepository. Klienten (tjänsten) är bara beroende av de metoder den behöver.

Överdriven abstraktion — gränssnitt för varje klass

Att skapa ett gränssnitt för varje klass utan verkligt behov av polymorfism är anti-mönstret Interface overkill. Tecken: gränssnittet har exakt en implementering och det finns inga planer på att lägga till alternativ i projektet. Lösning: lägg till gränssnitt endast när en andra implementeringsvariant uppstår eller behov av mock för testning.

Vanliga frågor

Vad är skillnaden mellan interface och abstract class i Java?

Interface definierar bara kontraktet (metodsignaturer), kan inte ha tillstånd och stödjer multiplelv nedärvning. Abstract class kan innehålla fält, konstruktorer och implementerade metoder, men en klass kan bara ärva en abstrakt klass. Från Java 8 har gränssnitt fått default och static metoder, vilket minskat gapet.

Kan ett gränssnitt ärva ett annat gränssnitt?

Ja, i Java och Kotlin stödjer gränssnitt nedärvning. public interface AdvancedRepository<T> extends Repository<T>, Pageable — ett gränssnitt som kombinerar två andra. Klassen som implementerar AdvancedRepository måste implementera alla metoder från båda föräldragränssnitten. Multiplelv nedärvning är endast tillåten för gränssnitt.

Vad är ett funktionellt gränssnitt i Java?

Funktionellt gränssnitt — ett gränssnitt med en abstrakt metod (SAM — Single Abstract Method). Annotationen @FunctionalInterface garanterar denna begränsning. Exempel: Runnable, Callable, Comparator, Consumer. Funktionella gränssnitt är grunden för lambda-uttryck i Java 8: () -> System.out.println() implementerar Runnable.

Varför behövs default-metoder i Java-gränssnitt?

Default-metoder gör det möjligt att lägga till nya metoder i ett gränssnitt utan att ändra alla implementerande klasser. Till exempel lade Java 8 till stream() i Collection som en default-metod. Utan denna mekanism skulle foreach(), stream() och andra metoder ha krävt ändring av tusentals klasser i JDK. Default — ett bakåtkompatibelt sätt att utvidga.

Hur löser Kotlin problemet med multiplelv nedärvning?

Kotlin förbjuder multiplelv nedärvning av klasser men tillåter multipel implementering av gränssnitt. Om två gränssnitt har en metod med samma signatur och standardimplementering, kräver kompilatorn explicit överskrivning med anrop av super<InterfaceName>.method(). Detta löser diamond problem på kompileringsnivå.

Sammanfattning

  • Interface — kontrakt som definierar metoder att implementera; huvudsaklig mekanism för polymorfism i Java och Kotlin
  • Java 8+ lade till default och static metoder i gränssnitt, vilket minskade gapet till abstrakta klasser
  • Kotlin interfaces stödjer abstrakta egenskaper, standardimplementeringar och delegering via by
  • Clean Architecture använder gränssnitt som gränser mellan Domain- och Data-lager
  • Interface Segregation Principle kräver uppdelning av stora gränssnitt i specialiserade
  • Funktionella gränssnitt (Single Abstract Method) — grund för lambda-uttryck och Stream API
  • Rekommendation: lägg till gränssnitt när en andra implementering uppstår eller behov av mock för testning

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också