Interface — egy szerződés, amely meghatározza az absztrakt metódusok halmazát, amelyeket egy osztálynak meg kell valósítania. Java és Kotlin környezetben az interfészek az absztrakció és polimorfizmus fő mechanizmusai. Java 8+ környezetben az interfészek tartalmazhatnak default és static metódusokat, Kotlin környezetben — alapértelmezett megvalósításokat. A Google Android Developers (2025) szerint az interfészek az Android projektek 90%-ában használatosak az architektúra rétegeinek — repozitóriumok, UseCase-ek és szolgáltatások — meghatározására.
Főbb pontok
Interface — egy referenciatípus, amely absztrakt metódusokat, konstansokat és alapértelmezett metódusokat tartalmaz. Az interfészt megvalósító osztály köteles biztosítani annak összes absztrakt metódusának megvalósítását. Java környezetben az interfész nem rendelkezhet állapottal (példány mezőkkel), Kotlin is betartja ezt a korlátozást, de támogatja a tulajdonságokat hozzáférési függvényekkel.
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;
// getterek és setterek
}
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();
}
// többi metódus
}Repository<T> — egy generikus interfész CRUD metódusokkal. Az count() alapértelmezett metódus egy felülírható alapértelmezett megvalósítást nyújt. A UserRepositoryImpl megvalósítja az interfészt, EntityManager-t használva az adatok eléréséhez. Ez a megközelítés lehetővé teszi az adatréteg tesztelését az interfész mock-ján keresztül valódi adatbázis nélkül.
Az interfész és absztrakt osztály közötti választás a közös állapot meglététől és a típusok közötti kapcsolatoktól függ. Az interfész szerződést határoz meg (mit tehet az osztály), az absztrakt osztály — közös megvalósítást (mi az osztály).
| Kritérium | Interface | Abstract Class |
|---|---|---|
| Állapot (mezők) | Csak static final konstansok | Igen, bármilyen mezők |
| Konstruktorok | Nem | Igen |
| Többszörös öröklés | Igen (implements) | Nem (extends egy) |
| Hozzáférési módosítók | public (Java 8-), default metódusok | Mind (private, protected, public) |
| Mikor használd | Szerződés nem rokon osztályok számára | Közös alap rokon osztályok számára |
A Clean Architecture rendszerben az interfészek a belső rétegben (domain), a megvalósítások pedig a külső rétegben (data) helyezkednek el. Ez lehetővé teszi a Függőségi Szabály betartását: a külső réteg függ a belsőtől, nem fordítva. Az absztrakt osztályokat gyakrabban használják sablon metódusokhoz (Template Method pattern).
A Kotlin interfészek rugalmasabbak, mint a Java-é: deklarálhatnak absztrakt tulajdonságokat és nyújthatnak metódusmegvalósításokat. A Java-tól eltérően a Kotlin támogatja a delegálást a by kulcsszó segítségével, ami jelentősen csökkenti a boilerplate kódot a Delegate minta megvalósításakor.
interface ApiService {
val baseUrl: String // absztrakt tulajdonság
suspend fun fetchData(): Result>
fun getEndpoint(path: String): String {
return "$baseUrl/$path" // alapértelmezett megvalósítás
}
}
class RetrofitApiService(
override val baseUrl: String
) : ApiService {
private val client = Retrofit.Builder()
.baseUrl(baseUrl)
.build()
override suspend fun fetchData(): Result>
{
// kérés megvalósítása
}
}
// Delegálás by segítségével
interface Logger {
fun log(message: String)
}
class ConsoleLogger : Logger {
override fun log(message: String) = println(message)
}
class UserService(logger: Logger) : Logger by loggerUserService megvalósítja a Logger interfészt delegálással (by). Az összes log() hívás a logger objektumhoz kerül átirányításra anélkül, hogy wrapper metódusokat kellene írni. Ez egy példa a kompozícióra, amely Java környezetben 5-10 sor boilerplate kódot igényelne.
Clean Architecture Android rendszerben egyértelműen rétegekre bontja az alkalmazást. Az interfészek határként szolgálnak a rétegek között: a Domain meghatározza a repozitóriumok és UseCase-ek interfészeit, a Data biztosítja a megvalósításokat. Ez lehetővé teszi a megvalósítások cseréjét az üzleti logika módosítása nélkül — kulcsfontosságú előny a Room-ról Firebase-re vagy REST-ről GraphQL-re történő migráció során.
// Domain réteg — interfész (szerződés)
interface UserRepository {
suspend fun getUser(id: String): User
suspend fun updateUser(user: User)
}
// Domain réteg — use case (függ az interfésztől)
class GetUserUseCase(
private val repository: UserRepository
) {
suspend operator fun invoke(id: String): Result {
return runCatching { repository.getUser(id) }
}
}
// Data réteg — megvalósítás
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 — interfész a domain rétegben. A GetUserUseCase az interfésztől függ, nem a megvalósítástól. A UserRepositoryImpl az adatrétegben megvalósítja az interfészt, kombinálva az API-t és a helyi adatbázist. Ez az architektúra lehetővé teszi a UseCase-ek tesztelését mock repozitóriummal adatbázis vagy hálózat konfigurációja nélkül.
A default metódusok Java 8-ban kerültek hozzáadásra az interfészek evolúciós bővítéséhez a visszafelé kompatibilitás megsértése nélkül. Ha az ArrayList nem valósította volna meg a Collection-hez hozzáadott új stream() metódust, a régi kód tovább működött volna. A static metódusok az interfészekben az interfészhez kapcsolódó segédfunkciók számára szolgálnak — alternatíva a segédosztályokhoz.
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();
}
// konstans
String CATEGORY = "transport";
}A default metódusok megoldják a gyémánt problémát (diamond problem): ha egy osztály két interfészt valósít meg ugyanazzal a default metódussal, a fordító explicit felülírást követel meg. A static metódusok az interfész nevével hívhatók — Vehicle.of("car"), példány nélkül.
Az interfészek tervezésének hibái törékeny kódhoz, tesztelési nehézségekhez és SOLID megsértéséhez vezetnek. Vizsgáljunk meg három gyakori problémát.
Egy 15+ metódust tartalmazó interfész megsérti az Interface Segregation Principle (ISP) elvet. Példa — a régi java.util.Dictionary 10+ metódussal. Megoldás: osszuk fel több kisebb interfészre — ReadableRepository, WritableRepository, SearchableRepository. Az ügyfél (szolgáltatás) csak a számára szükséges metódusoktól függ.
Interfész létrehozása minden osztályhoz valódi polimorfizmus igény nélkül az Interface overkill anti-minta. Jel: az interfésznek pontosan egy megvalósítása van, és a projektben nincsenek tervek alternatívák hozzáadására. Megoldás: csak akkor adj hozzá interfészt, amikor megjelenik a második megvalósítási változat, vagy szükség van mock-ra a tesztekhez.
Gyakran Ismételt Kérdések
Interface csak a szerződést (metódusok szignatúráit) határozza meg, nem rendelkezhet állapottal, és támogatja a többszörös öröklést. Abstract class tartalmazhat mezőket, konstruktorokat és megvalósított metódusokat, de egy osztály csak egy absztrakt osztályt örökölhet. Java 8-tól kezdve az interfészek default és static metódusokat kaptak, csökkentve a különbséget.
Igen, Java és Kotlin környezetben az interfészek támogatják az öröklést. public interface AdvancedRepository<T> extends Repository<T>, Pageable — egy interfész, amely két másikat egyesít. Az AdvancedRepository-t megvalósító osztálynak meg kell valósítania mindkét szülő interfész összes metódusát. Többszörös öröklés csak interfészek számára engedélyezett.
Funkcionális interfész — egy absztrakt metódussal rendelkező interfész (SAM — Single Abstract Method). A @FunctionalInterface annotáció garantálja ezt a korlátozást. Példák: Runnable, Callable, Comparator, Consumer. A funkcionális interfészek képezik a Java 8 lambda kifejezéseinek alapját: () -> System.out.println() megvalósítja a Runnable-t.
A default metódusok lehetővé teszik új metódusok hozzáadását egy interfészhez anélkül, hogy az összes megvalósító osztályt módosítani kellene. Például a Java 8 stream() metódusként adta hozzá a Collection-hez default metódusként. E mechanizmus nélkül a foreach(), stream() és más metódusok több ezer osztály módosítását igényelték volna a JDK-ban. A default — visszafelé kompatibilis bővítési mód.
Kotlin megtiltja az osztályok többszörös öröklését, de engedélyezi az interfészek többszörös megvalósítását. Ha két interfész rendelkezik azonos szignatúrájú és alapértelmezett megvalósítású metódussal, a fordító explicit felülírást követel meg a super<InterfaceName>.method() hívással. Ez megoldja a gyémánt problémát fordítási szinten.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is