Interface — je kontrakt definující sadu abstraktních metod, které musí třída implementovat. V Javě a Kotlinu jsou rozhraní hlavním mechanismem abstrakce a polymorfismu. V Javě 8+ mohou rozhraní obsahovat default a static metody, v Kotlinu — výchozí implementace. Podle Google Android Developers (2025) se rozhraní používají v 90% Android projektů k definování vrstev architektury — repozitářů, UseCase a služeb.
Hlavní body
Interface — je referenční typ obsahující abstraktní metody, konstanty a výchozí metody. Třída implementující rozhraní je povinna poskytnout implementaci všech jeho abstraktních metod. V Javě rozhraní nemůže mít stav (pole instance), Kotlin toto omezení také dodržuje, ale podporuje vlastnosti s přístupovými metodami.
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;
// gettry a settry
}
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();
}
// ostatní metody
}Repository<T> — generické rozhraní s CRUD metodami. Výchozí metoda count() poskytuje výchozí implementaci, kterou lze přepsat. UserRepositoryImpl implementuje rozhraní pomocí EntityManager pro přístup k datům. Tento přístup umožňuje testovat datovou vrstvu přes mock rozhraní bez skutečné databáze.
Volba mezi rozhraním a abstraktní třídou závisí na existenci společného stavu a vztazích mezi typy. Rozhraní definuje kontrakt (co třída může dělat), abstraktní třída — společnou implementaci (co třída je).
| Kritérium | Interface | Abstract Class |
|---|---|---|
| Stav (pole) | Pouze static final konstanty | Ano, libovolná pole |
| Konstruktory | Ne | Ano |
| Vícenásobná dědičnost | Ano (implements) | Ne (extends jeden) |
| Modifikátory přístupu | public (Java 8-), default metody | Všechny (private, protected, public) |
| Kdy použít | Kontrakt pro nesouvisející třídy | Společný základ pro příbuzné třídy |
V Clean Architecture jsou rozhraní umístěna ve vnitřní vrstvě (domain) a implementace — ve vnější (data). To umožňuje dodržovat Pravidlo závislosti: vnější vrstva závisí na vnitřní, ne naopak. Abstraktní třídy se častěji používají pro šablonové metody (Template Method pattern).
Kotlin rozhraní jsou flexibilnější než Java: mohou deklarovat abstraktní vlastnosti a poskytovat implementace metod. Na rozdíl od Javy Kotlin podporuje delegování pomocí klíčového slova by, což výrazně snižuje boilerplate při implementaci vzoru Delegate.
interface ApiService {
val baseUrl: String // abstraktní vlastnost
suspend fun fetchData(): Result>
fun getEndpoint(path: String): String {
return "$baseUrl/$path" // výchozí implementace
}
}
class RetrofitApiService(
override val baseUrl: String
) : ApiService {
private val client = Retrofit.Builder()
.baseUrl(baseUrl)
.build()
override suspend fun fetchData(): Result>
{
// implementace požadavku
}
}
// Delegování pomocí by
interface Logger {
fun log(message: String)
}
class ConsoleLogger : Logger {
override fun log(message: String) = println(message)
}
class UserService(logger: Logger) : Logger by loggerUserService implementuje rozhraní Logger pomocí delegování (by). Všechna volání log() jsou přesměrována na objekt logger bez psaní wrapper metod. Toto je příklad kompozice, která by v Javě vyžadovala 5-10 řádků boilerplate kódu.
Clean Architecture pro Android jasně rozděluje aplikaci na vrstvy. Rozhraní hrají roli hranic mezi vrstvami: Domain definuje rozhraní repozitářů a UseCase, Data poskytuje implementace. To umožňuje výměnu implementací bez změny obchodní logiky — klíčová výhoda při migraci z Room na Firebase nebo z REST na GraphQL.
// Domain vrstva — rozhraní (kontrakt)
interface UserRepository {
suspend fun getUser(id: String): User
suspend fun updateUser(user: User)
}
// Domain vrstva — use case (závisí na rozhraní)
class GetUserUseCase(
private val repository: UserRepository
) {
suspend operator fun invoke(id: String): Result {
return runCatching { repository.getUser(id) }
}
}
// Data vrstva — implementace
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 — rozhraní v domain vrstvě. GetUserUseCase závisí na rozhraní, ne na implementaci. UserRepositoryImpl v data vrstvě implementuje rozhraní a kombinuje API s lokální databází. Taková architektura umožňuje testovat UseCase s mock repozitářem bez konfigurace databáze nebo sítě.
Default metody byly přidány v Javě 8 pro evoluční rozšíření rozhraní bez porušení zpětné kompatibility. Pokud by ArrayList neimplementoval novou metodu stream() přidanou do Collection, starý kód by fungoval dál. Static metody v rozhraních slouží pro utility související s rozhraním — alternativa k utilitním třídám.
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();
}
// konstanta
String CATEGORY = "transport";
}Default metody řeší problém diamantu (diamond problem): pokud třída implementuje dvě rozhraní se stejnou default metodou, kompilátor vyžaduje explicitní přepsání. Static metody jsou volány přes jméno rozhraní — Vehicle.of("car"), bez instance.
Chyby při navrhování rozhraní vedou k křehkému kódu, obtížím při testování a porušení SOLID. Podívejme se na tři časté problémy.
Rozhraní s 15+ metodami porušuje Interface Segregation Principle (ISP). Příklad — staré java.util.Dictionary s 10+ metodami. Řešení: rozdělit na několik malých rozhraní — ReadableRepository, WritableRepository, SearchableRepository. Klient (služba) závisí pouze na metodách, které potřebuje.
Vytváření rozhraní pro každou třídu bez skutečné potřeby polymorfismu je anti-vzor Interface overkill. Znak: rozhraní má právě jednu implementaci a v projektu nejsou plány na přidání alternativ. Řešení: přidejte rozhraní pouze když se objeví druhá varianta implementace nebo potřeba mocku pro testy.
Často kladené otázky
Interface definuje pouze kontrakt (signatury metod), nemůže mít stav a podporuje vícenásobnou dědičnost. Abstract class může obsahovat pole, konstruktory a implementované metody, ale třída může dědit pouze jednu abstraktní třídu. Od Javy 8 rozhraní získala default a static metody, čímž se rozdíl zmenšil.
Ano, v Javě a Kotlinu rozhraní podporují dědičnost. public interface AdvancedRepository<T> extends Repository<T>, Pageable — rozhraní spojující dvě další. Třída implementující AdvancedRepository musí implementovat všechny metody obou rodičovských rozhraní. Vícenásobná dědičnost je povolena pouze pro rozhraní.
Funkcionální rozhraní — rozhraní s jednou abstraktní metodou (SAM — Single Abstract Method). Anotace @FunctionalInterface zaručuje toto omezení. Příklady: Runnable, Callable, Comparator, Consumer. Funkcionální rozhraní jsou základem lambda výrazů v Javě 8: () -> System.out.println() implementuje Runnable.
Default metody umožňují přidávat nové metody do rozhraní bez změny všech implementujících tříd. Například Java 8 přidala stream() do Collection jako default metodu. Bez tohoto mechanismu by foreach(), stream() a další metody vyžadovaly změnu tisíců tříd v JDK. Default — zpětně kompatibilní způsob rozšiřování.
Kotlin zakazuje vícenásobnou dědičnost tříd, ale povoluje vícenásobnou implementaci rozhraní. Pokud dvě rozhraní mají metodu se stejnou signaturou a výchozí implementací, kompilátor vyžaduje explicitní přepsání s voláním super<InterfaceName>.method(). To řeší problém diamantu na úrovni kompilace.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také