Interface: esența, contracte în Java și Kotlin pentru Android

Autor: IT Sectr Publicat: 2026-02-18 Timp de citire: 9 min

Interface — este un contract care definește un set de metode abstracte pe care o clasă trebuie să le implementeze. În Java și Kotlin, interfețele sunt mecanismul principal de abstractizare și polimorfism. În Java 8+ interfețele pot conține metode default și static, iar în Kotlin — implementări implicite. Conform Google Android Developers (2025), interfețele sunt utilizate în 90% din proiectele Android pentru definirea straturilor arhitecturii — repository-uri, UseCase și servicii.

Principalele

  • Interface — tip abstract care definește semnăturile metodelor fără implementare (înainte de Java 8)
  • Implements — cuvânt cheie care leagă o clasă de o interfață; o clasă poate implementa mai multe interfețe
  • Default method — metodă cu implementare în interfața Java, adăugată în Java 8 pentru compatibilitate inversă
  • Kotlin interface suportă proprietăți cu gettere și implementări de metode, înlocuind abstract class în multe scenarii
  • Markup interface — interfață goală folosită ca marcaj (Serializable, Cloneable, RandomAccess)

Ce este Interface în Java și Kotlin?

Interface — este un tip de referință care conține metode abstracte, constante și metode implicite. Clasa care implementează interfața este obligată să furnizeze implementarea tuturor metodelor sale abstracte. În Java, interfața nu poate avea stare (câmpuri de instanță), Kotlin respectă și el această restricție, dar suportă proprietăți cu accesori.

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;
    // gettere și settere
}

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();
    }
    // celelalte metode
}

Repository<T> — o interfață generică cu metode CRUD. Metoda implicită count() oferă o implementare implicită care poate fi suprascrisă. UserRepositoryImpl implementează interfața, folosind EntityManager pentru accesul la date. Această abordare permite testarea stratului de date printr-un mock al interfeței fără o bază de date reală.

Interface vs abstract class: când să alegi ce

Alegerea între interfață și clasa abstractă depinde de existența stării comune și a relațiilor dintre tipuri. Interfața definește un contract (ce poate face clasa), clasa abstractă — o implementare comună (ce este clasa).

CriteriuInterfaceAbstract Class
Stare (câmpuri)Doar constante static finalDa, orice câmpuri
ConstructoriNuDa
Moștenire multiplăDa (implements)Nu (extends unul)
Modificatori de accespublic (Java 8-), metode defaultToate (private, protected, public)
Când să foloseștiContract pentru clase neînruditeBază comună pentru clase înrudite

În Clean Architecture interfețele sunt plasate în stratul interior (domain), iar implementările — în stratul exterior (data). Aceasta permite respectarea Regulii Dependenței: stratul exterior depinde de cel interior, dar nu invers. Clasele abstracte sunt folosite mai des pentru metode șablon (Template Method pattern).

Interface în Kotlin: proprietăți și delegați

Interfețele Kotlin sunt mai flexible decât în Java: pot declara proprietăți abstracte și pot oferi implementări de metode. Spre deosebire de Java, Kotlin suportă delegarea prin cuvântul cheie by, ceea ce reduce semnificativ boilerplate-ul la implementarea pattern-ului Delegate.

Kotlin
interface ApiService {
    val baseUrl: String  // proprietate abstractă
    
    suspend fun fetchData(): Result>
    
    fun getEndpoint(path: String): String {
        return "$baseUrl/$path"  // implementare implicită
    }
}

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

// Delegare prin 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 implementează interfața Logger prin delegare (by). Toate apelurile log() sunt redirecționate către obiectul logger fără a scrie metode wrapper. Acesta este un exemplu de compoziție care în Java ar necesita 5-10 linii de cod boilerplate.

Interface în Clean Architecture Android

Clean Architecture pentru Android separă clar aplicația în straturi. Interfețele joacă rolul de granițe între straturi: Domain definește interfețele repository-urilor și UseCase-urilor, Data oferă implementări. Aceasta permite înlocuirea implementărilor fără a modifica logica de business — un avantaj cheie la migrarea de la Room la Firebase sau de la REST la GraphQL.

Kotlin
// Stratul Domain — interfață (contract)
interface UserRepository {
    suspend fun getUser(id: String): User
    suspend fun updateUser(user: User)
}

// Stratul Domain — use case (depinde de interfață)
class GetUserUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(id: String): Result {
        return runCatching { repository.getUser(id) }
    }
}

// Stratul Data — implementare
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 — interfață în stratul domain. GetUserUseCase depinde de interfață, nu de implementare. UserRepositoryImpl în stratul data implementează interfața, combinând API și baza de date locală. O astfel de arhitectură permite testarea UseCase-urilor cu un repository mock fără configurarea bazei de date sau a rețelei.

Metode Default și static în Java 8+

Metodele default au fost adăugate în Java 8 pentru extinderea evolutivă a interfețelor fără a încălca compatibilitatea inversă. Dacă ArrayList nu ar fi implementat noua metodă stream() adăugată în Collection, codul vechi ar fi continuat să funcționeze. Metodele static în interfețe servesc pentru utilități legate de interfață — o alternativă la clasele utilitare.

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

Metodele default rezolvă problema diamantului (diamond problem): dacă o clasă implementează două interfețe cu aceeași metodă default, compilatorul solicită suprascrierea explicită. Metodele static sunt apelate prin numele interfeței — Vehicle.of("car"), fără instanță.

Greșeli tipice la proiectarea interfețelor

Greșelile în proiectarea interfețelor duc la cod fragil, dificultăți de testare și încălcări ale SOLID. Să analizăm trei probleme frecvente.

Interface Pollution — prea multe metode într-o singură interfață

O interfață cu 15+ metode încalcă Interface Segregation Principle (ISP). Exemplu — vechiul java.util.Dictionary cu 10+ metode. Soluție: împărțirea în mai multe interfețe mici — ReadableRepository, WritableRepository, SearchableRepository. Clientul (serviciul) depinde doar de metodele de care are nevoie.

Abstractizare excesivă — interfață pentru fiecare clasă

Crearea unei interfețe pentru fiecare clasă fără nevoia reală de polimorfism este anti-pattern-ul Interface overkill. Semn: interfața are exact o implementare și nu există planuri de adăugare a alternativelor în proiect. Soluție: adăugați interfață doar când apare a doua variantă de implementare sau nevoia de mock pentru teste.

Întrebări frecvente

Cu ce se deosebește interface de abstract class în Java?

Interface definește doar contractul (semnăturile metodelor), nu poate avea stare și suportă moștenirea multiplă. Abstract class poate conține câmpuri, constructori și metode implementate, dar o clasă poate moșteni o singură clasă abstractă. Începând cu Java 8, interfețele au primit metode default și static, reducând diferența.

Poate o interfață să moștenească o altă interfață?

Da, în Java și Kotlin interfețele suportă moștenirea. public interface AdvancedRepository<T> extends Repository<T>, Pageable — o interfață care combină alte două. Clasa care implementează AdvancedRepository trebuie să implementeze toate metodele ambelor interfețe părinte. Moștenirea multiplă este permisă doar pentru interfețe.

Ce este o interfață funcțională în Java?

Interfața funcțională — o interfață cu o singură metodă abstractă (SAM — Single Abstract Method). Adnotarea @FunctionalInterface garantează această restricție. Exemple: Runnable, Callable, Comparator, Consumer. Interfețele funcționale stau la baza expresiilor lambda din Java 8: () -> System.out.println() implementează Runnable.

De ce sunt necesare metodele default în interfețele Java?

Metodele default permit adăugarea de noi metode într-o interfață fără a modifica toate clasele care o implementează. De exemplu, Java 8 a adăugat stream() în Collection ca metodă default. Fără acest mecanism, foreach(), stream() și alte metode ar fi necesitat modificarea a mii de clase în JDK. Default — o modalitate compatibilă invers de extindere.

Cum rezolvă Kotlin problema moștenirii multiple?

Kotlin interzice moștenirea multiplă a claselor, dar permite implementarea multiplă a interfețelor. Dacă două interfețe au o metodă cu aceeași semnătură și implementare implicită, compilatorul solicită suprascrierea explicită cu apelul super<InterfaceName>.method(). Aceasta rezolvă problema diamantului la nivel de compilare.

Concluzii

  • Interface — contract care definește metodele de implementat; mecanismul principal de polimorfism în Java și Kotlin
  • Java 8+ a adăugat metode default și static în interfețe, reducând diferența față de clasele abstracte
  • Kotlin interfaces suportă proprietăți abstracte, implementări implicite și delegare prin by
  • Clean Architecture folosește interfețe ca granițe între straturile Domain și Data
  • Interface Segregation Principle cere împărțirea interfețelor mari în interfețe specializate
  • Interfețele funcționale (Single Abstract Method) — baza expresiilor lambda și Stream API
  • Recomandare: adăugați o interfață când apare a doua implementare sau nevoia de mock pentru testare

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și