Interface: esensi, kontrak di Java dan Kotlin untuk Android

Penulis: IT Sectr Diterbitkan: 2026-02-18 Waktu membaca: 9 mnt

Interface — adalah kontrak yang mendefinisikan sekumpulan metode abstrak yang harus diimplementasikan oleh kelas. Di Java dan Kotlin, antarmuka adalah mekanisme utama abstraksi dan polimorfisme. Di Java 8+ antarmuka dapat berisi metode default dan static, di Kotlin — implementasi default. Menurut Google Android Developers (2025), antarmuka digunakan di 90% proyek Android untuk mendefinisikan lapisan arsitektur — repositori, UseCase, dan layanan.

Poin Utama

  • Interface — tipe abstrak yang mendefinisikan tanda tangan metode tanpa implementasi (sebelum Java 8)
  • Implements — kata kunci yang menghubungkan kelas dengan antarmuka; kelas dapat mengimplementasikan beberapa antarmuka
  • Default method — metode dengan implementasi di antarmuka Java, ditambahkan di Java 8 untuk kompatibilitas mundur
  • Kotlin interface mendukung properti dengan getter dan implementasi metode, menggantikan abstract class di banyak skenario
  • Markup interface — antarmuka kosong yang digunakan sebagai penanda (Serializable, Cloneable, RandomAccess)

Apa itu Interface di Java dan Kotlin?

Interface — adalah tipe referensi yang berisi metode abstrak, konstanta, dan metode default. Kelas yang mengimplementasikan antarmuka wajib menyediakan implementasi dari semua metode abstraknya. Di Java, antarmuka tidak dapat memiliki status (bidang instans), Kotlin juga mengikuti batasan ini, tetapi mendukung properti dengan aksesor.

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

Repository<T> — antarmuka generik dengan metode CRUD. Metode default count() menyediakan implementasi default yang dapat ditimpa. UserRepositoryImpl mengimplementasikan antarmuka menggunakan EntityManager untuk akses data. Pendekatan ini memungkinkan pengujian lapisan data melalui mock antarmuka tanpa database nyata.

Interface vs abstract class: kapan memilih apa

Pilihan antara antarmuka dan kelas abstrak tergantung pada keberadaan status bersama dan hubungan antar tipe. Antarmuka mendefinisikan kontrak (apa yang bisa dilakukan kelas), kelas abstrak — implementasi bersama (apa adanya kelas).

KriteriaInterfaceAbstract Class
Status (bidang)Hanya konstanta static finalYa, bidang apa pun
KonstruktorTidakYa
Warisan bergandaYa (implements)Tidak (extends satu)
Pengubah aksespublic (Java 8-), metode defaultSemua (private, protected, public)
Kapan digunakanKontrak untuk kelas yang tidak terkaitBasis bersama untuk kelas terkait

Di Clean Architecture, antarmuka ditempatkan di lapisan dalam (domain), dan implementasi — di lapisan luar (data). Ini memungkinkan kepatuhan terhadap Aturan Ketergantungan: lapisan luar bergantung pada lapisan dalam, bukan sebaliknya. Kelas abstrak lebih sering digunakan untuk metode template (Template Method pattern).

Interface di Kotlin: properti dan delegasi

Antarmuka Kotlin lebih fleksibel daripada Java: mereka dapat mendeklarasikan properti abstrak dan menyediakan implementasi metode. Tidak seperti Java, Kotlin mendukung delegasi melalui kata kunci by, yang secara signifikan mengurangi boilerplate saat mengimplementasikan pola Delegate.

Kotlin
interface ApiService {
    val baseUrl: String  // properti abstrak
    
    suspend fun fetchData(): Result>
    
    fun getEndpoint(path: String): String {
        return "$baseUrl/$path"  // implementasi default
    }
}

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

// Delegasi melalui 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 mengimplementasikan antarmuka Logger melalui delegasi (by). Semua panggilan log() dialihkan ke objek logger tanpa menulis metode wrapper. Ini adalah contoh komposisi yang di Java akan membutuhkan 5-10 baris kode boilerplate.

Interface di Clean Architecture Android

Clean Architecture untuk Android secara jelas membagi aplikasi menjadi beberapa lapisan. Antarmuka berperan sebagai batas antar lapisan: Domain mendefinisikan antarmuka repositori dan UseCase, Data menyediakan implementasi. Ini memungkinkan penggantian implementasi tanpa mengubah logika bisnis — keuntungan utama saat migrasi dari Room ke Firebase atau dari REST ke GraphQL.

Kotlin
// Lapisan Domain — antarmuka (kontrak)
interface UserRepository {
    suspend fun getUser(id: String): User
    suspend fun updateUser(user: User)
}

// Lapisan Domain — use case (bergantung pada antarmuka)
class GetUserUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(id: String): Result {
        return runCatching { repository.getUser(id) }
    }
}

// Lapisan Data — implementasi
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 — antarmuka di lapisan domain. GetUserUseCase bergantung pada antarmuka, bukan pada implementasi. UserRepositoryImpl di lapisan data mengimplementasikan antarmuka, menggabungkan API dan basis data lokal. Arsitektur semacam itu memungkinkan pengujian UseCase dengan repositori mock tanpa konfigurasi basis data atau jaringan.

Metode Default dan static di Java 8+

Metode default ditambahkan di Java 8 untuk memperluas antarmuka secara evolusioner tanpa melanggar kompatibilitas mundur. Jika ArrayList tidak mengimplementasikan metode stream() baru yang ditambahkan ke Collection, kode lama akan terus berfungsi. Metode static di antarmuka berfungsi untuk utilitas yang terkait dengan antarmuka — alternatif untuk kelas utilitas.

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

Metode default memecahkan masalah diamond (diamond problem): jika sebuah kelas mengimplementasikan dua antarmuka dengan metode default yang sama, kompiler memerlukan penimpaan eksplisit. Metode static dipanggil melalui nama antarmuka — Vehicle.of("car"), tanpa instance.

Kesalahan umum dalam merancang antarmuka

Kesalahan dalam merancang antarmuka menyebabkan kode rapuh, kesulitan pengujian, dan pelanggaran SOLID. Mari kita bahas tiga masalah umum.

Interface Pollution — terlalu banyak metode dalam satu antarmuka

Antarmuka dengan 15+ metode melanggar Interface Segregation Principle (ISP). Contoh — java.util.Dictionary lama dengan 10+ metode. Solusi: bagi menjadi beberapa antarmuka kecil — ReadableRepository, WritableRepository, SearchableRepository. Klien (layanan) hanya bergantung pada metode yang dibutuhkannya.

Abstraksi berlebihan — antarmuka untuk setiap kelas

Membuat antarmuka untuk setiap kelas tanpa kebutuhan nyata akan polimorfisme adalah anti-pola Interface overkill. Tanda: antarmuka memiliki tepat satu implementasi dan tidak ada rencana untuk menambahkan alternatif di proyek. Solusi: tambahkan antarmuka hanya ketika varian implementasi kedua muncul atau ada kebutuhan mock untuk pengujian.

Pertanyaan yang Sering Diajukan

Apa perbedaan interface dengan abstract class di Java?

Interface hanya mendefinisikan kontrak (tanda tangan metode), tidak dapat memiliki status, dan mendukung warisan berganda. Abstract class dapat berisi bidang, konstruktor, dan metode yang diimplementasikan, tetapi kelas hanya dapat mewarisi satu kelas abstrak. Sejak Java 8, antarmuka mendapatkan metode default dan static, memperkecil kesenjangan.

Bisakah antarmuka mewarisi antarmuka lain?

Ya, di Java dan Kotlin antarmuka mendukung warisan. public interface AdvancedRepository<T> extends Repository<T>, Pageable — antarmuka yang menggabungkan dua lainnya. Kelas yang mengimplementasikan AdvancedRepository harus mengimplementasikan semua metode dari kedua antarmuka induk. Warisan berganda hanya diizinkan untuk antarmuka.

Apa itu antarmuka fungsional di Java?

Antarmuka fungsional — antarmuka dengan satu metode abstrak (SAM — Single Abstract Method). Anotasi @FunctionalInterface menjamin batasan ini. Contoh: Runnable, Callable, Comparator, Consumer. Antarmuka fungsional adalah dasar dari ekspresi lambda Java 8: () -> System.out.println() mengimplementasikan Runnable.

Mengapa metode default diperlukan di antarmuka Java?

Metode default memungkinkan penambahan metode baru ke antarmuka tanpa mengubah semua kelas yang mengimplementasikannya. Misalnya, Java 8 menambahkan stream() ke Collection sebagai metode default. Tanpa mekanisme ini, foreach(), stream(), dan metode lainnya akan memerlukan perubahan ribuan kelas di JDK. Default — cara yang kompatibel mundur untuk memperluas.

Bagaimana Kotlin memecahkan masalah warisan berganda?

Kotlin melarang warisan berganda kelas, tetapi mengizinkan implementasi berganda antarmuka. Jika dua antarmuka memiliki metode dengan tanda tangan dan implementasi default yang sama, kompiler memerlukan penimpaan eksplisit dengan panggilan super<InterfaceName>.method(). Ini memecahkan masalah diamond pada tingkat kompilasi.

Ringkasan

  • Interface — kontrak yang mendefinisikan metode untuk diimplementasikan; mekanisme utama polimorfisme di Java dan Kotlin
  • Java 8+ menambahkan metode default dan static ke antarmuka, memperkecil kesenjangan dengan kelas abstrak
  • Kotlin interfaces mendukung properti abstrak, implementasi default, dan delegasi melalui by
  • Clean Architecture menggunakan antarmuka sebagai batas antara lapisan Domain dan Data
  • Interface Segregation Principle mengharuskan pemisahan antarmuka besar menjadi antarmuka khusus
  • Antarmuka fungsional (Single Abstract Method) — dasar ekspresi lambda dan Stream API
  • Rekomendasi: tambahkan antarmuka saat implementasi kedua muncul atau ada kebutuhan mock untuk pengujian

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga