Interface #
Bayangkan kamu sedang membangun sistem pembayaran yang mendukung transfer bank, kartu kredit, dan dompet digital. Setiap metode pembayaran punya cara kerja yang sama sekali berbeda di balik layar, tapi dari sudut pandang program yang memakainya, semuanya harus bisa melakukan satu hal: memproses pembayaran. Interface adalah alat yang menyelesaikan masalah ini. Interface mendefinisikan apa yang harus bisa dilakukan suatu kelas, tanpa peduli bagaimana caranya. Dengan interface, kamu bisa menulis kode yang bekerja dengan semua metode pembayaran sekaligus — bahkan dengan metode yang belum ada saat ini. Artikel ini membahas cara kerja interface dari deklarasi, implementasi, multiple interface, default method, static method, hingga kapan memilih interface dibanding kelas abstrak.
Konsep Dasar #
Interface adalah kontrak. Kelas yang menyatakan implements SuatuInterface berjanji untuk menyediakan implementasi semua metode yang didefinisikan di interface tersebut. Kalau janjinya tidak dipenuhi, program tidak akan bisa dikompilasi.
Ada beberapa sifat bawaan interface yang perlu kamu pahami sebelum mulai menulis kode:
| Elemen | Sifat Default | Keterangan |
|---|---|---|
| Metode (sebelum Java 8) | public abstract | Tidak punya body, wajib diimplementasikan |
Metode default (Java 8+) | public | Punya body, bisa di-override tapi tidak wajib |
Metode static (Java 8+) | public static | Milik interface, tidak diwarisi ke implementor |
| Field / variabel | public static final | Konstanta, tidak bisa diubah |
Interface tidak bisa diinstansiasi langsung — kamu tidak bisa menulis new Hewan() kalau Hewan adalah interface. Interface hanya bisa dipakai lewat kelas yang mengimplementasikannya.
flowchart TD
A["«interface»\nHewan\n─────────\nsuara()\nmakan()"] --> B["Kucing\nimplements Hewan"]
A --> C["Anjing\nimplements Hewan"]
A --> D["Harimau\nimplements Hewan"]
E[Kode Klien] -->|"Hewan h = new Kucing()"| ADeklarasi Interface #
Interface dideklarasikan dengan kata kunci interface, bukan class. Metode di dalamnya tidak punya body — hanya tanda tangan (nama, parameter, return type).
Sintaks Dasar #
// Deklarasi interface: hanya kontrak, tanpa implementasi
public interface Hewan {
void suara(); // secara implisit: public abstract void suara()
void makan(); // secara implisit: public abstract void makan()
String getNama();
}
Konvensi penamaan interface di Java menggunakan kata sifat atau kata benda yang mendeskripsikan kemampuan: Comparable, Serializable, Runnable, Closeable. Ini berbeda dengan kelas yang biasanya dinamai kata benda konkret (Anjing, AkunBank).
Di Java, semua field yang dideklarasikan di dalam interface secara otomatis bersifat public static final — artinya konstanta. Kamu tidak bisa punya atribut instance di interface. Ini adalah perbedaan fundamental dengan kelas abstrak.Implementasi Interface #
Kelas mengimplementasikan interface dengan kata kunci implements. Semua metode abstrak di interface wajib disediakan implementasinya — tidak boleh ada yang terlewat.
Mengimplementasikan Semua Metode #
public class Kucing implements Hewan {
private String nama;
public Kucing(String nama) {
this.nama = nama;
}
// Wajib: implementasi semua metode dari interface Hewan
@Override
public void suara() {
System.out.println(nama + " bersuara: Meong!");
}
@Override
public void makan() {
System.out.println(nama + " makan ikan.");
}
@Override
public String getNama() {
return nama;
}
}
public class Anjing implements Hewan {
private String nama;
public Anjing(String nama) {
this.nama = nama;
}
@Override
public void suara() {
System.out.println(nama + " bersuara: Guk guk!");
}
@Override
public void makan() {
System.out.println(nama + " makan tulang.");
}
@Override
public String getNama() {
return nama;
}
}
Jangan Lewatkan Metode #
// ANTI-PATTERN: kelas mengimplementasikan interface tapi tidak mengisi semua metode
public class Burung implements Hewan {
@Override
public void suara() {
System.out.println("Cuit cuit!");
}
// makan() dan getNama() tidak diimplementasikan
// → error: Burung is not abstract and does not override abstract method makan() in Hewan
}
// BENAR: implementasikan semua metode, atau jadikan kelas tersebut abstract
public class Burung implements Hewan {
private String nama;
public Burung(String nama) {
this.nama = nama;
}
@Override
public void suara() {
System.out.println(nama + " bersuara: Cuit cuit!");
}
@Override
public void makan() {
System.out.println(nama + " makan biji-bijian.");
}
@Override
public String getNama() {
return nama;
}
}
Anotasi @Override sangat disarankan saat mengimplementasikan metode dari interface. Compiler akan memberikan error jika nama atau signature metode salah ketik — ini menghindari bug yang sulit dilacak.
Interface sebagai Tipe Data #
Salah satu kegunaan terpenting interface adalah sebagai tipe data. Variabel bertipe interface bisa menampung objek dari kelas mana pun yang mengimplementasikannya. Ini adalah pondasi dari polimorfisme di Java.
Deklarasi Variabel Bertipe Interface #
// Tipe variabel adalah interface, bukan kelas konkret
Hewan kucing = new Kucing("Luna");
Hewan anjing = new Anjing("Rex");
Hewan burung = new Burung("Tweety");
kucing.suara(); // Output: Luna bersuara: Meong!
anjing.suara(); // Output: Rex bersuara: Guk guk!
burung.suara(); // Output: Tweety bersuara: Cuit cuit!
Parameter Metode Bertipe Interface #
Pola ini sangat umum di kode produksi. Daripada mengikat kode ke kelas konkret, kamu menulis metode yang bekerja dengan interface. Hasilnya: menambah jenis hewan baru di masa depan tidak mengubah satu baris pun di bunyikanSemua().
public class Main {
public static void main(String[] args) {
bunyikanSemua(new Hewan[]{
new Kucing("Luna"),
new Anjing("Rex"),
new Burung("Tweety")
});
}
// Parameter bertipe interface: menerima objek kelas apa pun yang implements Hewan
static void bunyikanSemua(Hewan[] hewanList) {
for (Hewan h : hewanList) {
System.out.print(h.getNama() + " → ");
h.suara();
}
}
}
sequenceDiagram
participant Main
participant bunyikanSemua
participant kucing as kucing (Kucing)
participant anjing as anjing (Anjing)
participant burung as burung (Burung)
Main->>bunyikanSemua: bunyikanSemua([kucing, anjing, burung])
bunyikanSemua->>kucing: getNama(), suara()
kucing-->>bunyikanSemua: "Luna", "Meong!"
bunyikanSemua->>anjing: getNama(), suara()
anjing-->>bunyikanSemua: "Rex", "Guk guk!"
bunyikanSemua->>burung: getNama(), suara()
burung-->>bunyikanSemua: "Tweety", "Cuit cuit!"Multiple Interface #
Java tidak mengizinkan satu kelas mewarisi lebih dari satu kelas (extends hanya bisa ke satu kelas). Tapi sebuah kelas boleh mengimplementasikan lebih dari satu interface. Ini cara Java mendapatkan fleksibilitas multiple inheritance tanpa ambiguitas yang datang bersamanya.
Mendefinisikan Beberapa Interface #
public interface Terbang {
void terbang();
int getKetinggianMaksimal(); // dalam meter
}
public interface Berenang {
void berenang();
int getKecepatanRenang(); // dalam km/h
}
public interface Berlari {
void berlari();
int getKecepatanLari(); // dalam km/h
}
Satu Kelas, Banyak Interface #
// Bebek bisa terbang, berenang, dan berlari — implement semua tiga interface
public class Bebek implements Terbang, Berenang, Berlari {
private String nama;
public Bebek(String nama) { this.nama = nama; }
@Override
public void terbang() {
System.out.println(nama + " terbang rendah di atas danau.");
}
@Override
public int getKetinggianMaksimal() { return 100; }
@Override
public void berenang() {
System.out.println(nama + " berenang di danau.");
}
@Override
public int getKecepatanRenang() { return 3; }
@Override
public void berlari() {
System.out.println(nama + " berlari tergesa-gesa.");
}
@Override
public int getKecepatanLari() { return 5; }
}
Menggunakan Objek Lewat Berbagai Tipe Interface #
Bebek bebek = new Bebek("Donald");
// Bebek bisa dipakai sebagai Terbang, Berenang, atau Berlari
Terbang terbang = bebek;
Berenang renang = bebek;
terbang.terbang();
renang.berenang();
flowchart LR
A["«interface»\nTerbang"] --> C["Bebek\nimplements\nTerbang, Berenang, Berlari"]
B["«interface»\nBerenang"] --> C
D["«interface»\nBerlari"] --> C
A --> E["Elang\nimplements Terbang"]Ketika satu kelas mengimplementasikan dua interface yang keduanya punya default method dengan nama yang sama, Java tidak bisa memutuskan mana yang dipakai. Kamu wajib meng-override metode tersebut di kelas dan menentukan sendiri implementasinya — kalau tidak, program tidak akan terkompilasi.Default Method #
Sejak Java 8, interface boleh punya metode dengan implementasi — disebut default method. Ini dirancang untuk memecahkan satu masalah nyata: bagaimana cara menambah metode baru ke interface yang sudah ada tanpa merusak semua kelas yang sudah mengimplementasikannya.
Menambah Default Method ke Interface #
public interface Notifikasi {
void kirimPesan(String pesan);
String getTujuan();
// ANTI-PATTERN: menambah metode abstrak baru ke interface yang sudah dipakai banyak kelas
// void kirimPesanDenganSubjek(String subjek, String pesan);
// → semua kelas implementor error: wajib implementasi metode baru
// BENAR: gunakan default method agar tidak merusak kelas yang ada
default void kirimPesanDenganSubjek(String subjek, String pesan) {
kirimPesan("[" + subjek + "] " + pesan);
}
default void kirimPengingat(String pesan) {
kirimPesan("PENGINGAT: " + pesan);
}
}
Menggunakan dan Meng-override Default Method #
Kelas lama tidak perlu diubah sama sekali — ia otomatis mewarisi default method. Kelas baru bisa meng-override jika perlu perilaku yang berbeda.
// Kelas lama: tidak perlu diubah sama sekali
public class NotifikasiEmail implements Notifikasi {
private String emailTujuan;
public NotifikasiEmail(String emailTujuan) { this.emailTujuan = emailTujuan; }
@Override
public void kirimPesan(String pesan) {
System.out.println("Email ke " + emailTujuan + ": " + pesan);
}
@Override
public String getTujuan() { return emailTujuan; }
// kirimPesanDenganSubjek() dan kirimPengingat() tersedia lewat default method
}
// Kelas baru: override default method karena SMS punya format berbeda
public class NotifikasiSMS implements Notifikasi {
private String nomorTujuan;
public NotifikasiSMS(String nomorTujuan) { this.nomorTujuan = nomorTujuan; }
@Override
public void kirimPesan(String pesan) {
System.out.println("SMS ke " + nomorTujuan + ": " + pesan);
}
@Override
public String getTujuan() { return nomorTujuan; }
@Override
public void kirimPesanDenganSubjek(String subjek, String pesan) {
kirimPesan(subjek.toUpperCase() + " - " + pesan);
}
}
Notifikasi email = new NotifikasiEmail("[email protected]");
Notifikasi sms = new NotifikasiSMS("+6281234567890");
email.kirimPesanDenganSubjek("Tagihan", "Tagihan bulan ini sudah jatuh tempo.");
// Output: Email ke [email protected]: [Tagihan] Tagihan bulan ini sudah jatuh tempo.
sms.kirimPesanDenganSubjek("Tagihan", "Tagihan bulan ini sudah jatuh tempo.");
// Output: SMS ke +6281234567890: TAGIHAN - Tagihan bulan ini sudah jatuh tempo.
Static Method #
Selain default method, Java 8 juga memperkenalkan static method di interface. Berbeda dengan default method, static method tidak diwarisi oleh kelas implementor — ia dipanggil langsung lewat nama interface.
Mendefinisikan Static Method #
public interface Validator {
boolean validasi(String input);
// Static method: utilitas yang terkait dengan interface ini
static boolean tidakKosong(String nilai) {
return nilai != null && !nilai.trim().isEmpty();
}
static boolean panjangValid(String nilai, int min, int max) {
if (!tidakKosong(nilai)) return false;
int panjang = nilai.trim().length();
return panjang >= min && panjang <= max;
}
}
Memanggil Static Method #
public class ValidatorEmail implements Validator {
@Override
public boolean validasi(String email) {
if (!Validator.tidakKosong(email)) return false;
return email.contains("@") && email.contains(".");
}
}
public class Main {
public static void main(String[] args) {
Validator emailValidator = new ValidatorEmail();
System.out.println(emailValidator.validasi("[email protected]")); // true
System.out.println(emailValidator.validasi("bukan-email")); // false
// Static method dipanggil langsung lewat nama interface
System.out.println(Validator.tidakKosong("")); // false
System.out.println(Validator.tidakKosong("abc")); // true
// ANTI-PATTERN: mencoba memanggil static method lewat instance
// emailValidator.tidakKosong("abc"); // error: tidak bisa
}
}
Interface di Java Standard Library #
Interface bukan hanya konsep abstrak — Java standard library sendiri dibangun di atasnya. Memahami interface yang sudah ada membantu kamu menulis kode yang lebih idiomatis dan interoperable.
| Interface | Package | Kegunaan |
|---|---|---|
Comparable<T> | java.lang | Mendefinisikan urutan natural objek (compareTo) |
Comparator<T> | java.util | Mendefinisikan urutan kustom dari luar kelas |
Iterable<T> | java.lang | Membuat objek bisa dipakai di for-each loop |
Runnable | java.lang | Mendefinisikan task yang bisa dijalankan di thread |
Callable<V> | java.util.concurrent | Seperti Runnable tapi bisa return nilai dan throw exception |
Closeable | java.io | Mendefinisikan resource yang bisa ditutup (try-with-resources) |
Contoh: Mengimplementasikan Comparable #
Kelas Produk yang mengimplementasikan Comparable agar bisa diurutkan otomatis oleh Collections.sort():
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Produk implements Comparable<Produk> {
private String nama;
private double harga;
public Produk(String nama, double harga) {
this.nama = nama;
this.harga = harga;
}
// Implementasi Comparable: urutkan berdasarkan harga (ascending)
@Override
public int compareTo(Produk lain) {
return Double.compare(this.harga, lain.harga);
}
@Override
public String toString() {
return nama + " (Rp " + harga + ")";
}
}
public class Main {
public static void main(String[] args) {
List<Produk> daftarProduk = new ArrayList<>();
daftarProduk.add(new Produk("Laptop", 12000000));
daftarProduk.add(new Produk("Mouse", 150000));
daftarProduk.add(new Produk("Monitor", 3500000));
daftarProduk.add(new Produk("Keyboard", 450000));
// Collections.sort() bekerja karena Produk implements Comparable
Collections.sort(daftarProduk);
for (Produk p : daftarProduk) {
System.out.println(p);
}
// Output (urut dari termurah):
// Mouse (Rp 150000.0)
// Keyboard (Rp 450000.0)
// Monitor (Rp 3500000.0)
// Laptop (Rp 12000000.0)
}
}
Interface vs Kelas Abstrak #
Ini adalah pertanyaan yang sering muncul. Keduanya bisa mendefinisikan kontrak, tapi punya karakteristik yang berbeda. Pilih berdasarkan kebutuhan spesifik, bukan preferensi.
| Aspek | Interface | Kelas Abstrak |
|---|---|---|
| Instantiasi | Tidak bisa | Tidak bisa |
| Multiple inheritance | ✓ Satu kelas bisa implement banyak | ✗ Hanya satu extends |
| Atribut instance | ✗ Tidak bisa (hanya konstanta) | ✓ Bisa punya atribut |
| Konstruktor | ✗ Tidak ada | ✓ Ada |
| Metode konkret | ✓ Lewat default / static | ✓ Metode biasa |
| Access modifier metode | Hanya public | Bisa public, protected, private |
| Hubungan | “Bisa melakukan” (kemampuan) | “Adalah jenis dari” (hierarki) |
Decision Tree #
flowchart TD
A{Apakah ada state\natribut bersama?} -- Ya --> B[Kelas Abstrak]
A -- Tidak --> C{Perlu multiple\ninheritance?}
C -- Ya --> D[Interface]
C -- Tidak --> E{Hubungannya\n'adalah' atau 'bisa'?}
E -- "'adalah'\n(Anjing adalah Hewan)" --> F[Kelas Abstrak]
E -- "'bisa'\n(Burung bisa Terbang)" --> G[Interface]Kombinasi Keduanya #
Kelas abstrak dan interface bisa dipakai bersamaan. Kelas abstrak mendefinisikan hierarki dan state, sementara interface menambah kemampuan lintas hierarki.
// Kelas abstrak: hierarki dan state bersama
abstract class Karyawan {
protected String nama;
protected double gajiBasis;
public Karyawan(String nama, double gajiBasis) {
this.nama = nama;
this.gajiBasis = gajiBasis;
}
public String getNama() { return nama; }
abstract double hitungGaji();
}
// Interface: kemampuan tambahan yang bisa dimiliki siapa saja
interface BisaLembur {
double hitungLembur(int jamLembur);
}
interface BisaBonus {
double hitungBonus(double persentase);
}
// Karyawan senior: punya hierarki dari KaryawanTetap + kemampuan lembur & bonus
class KaryawanTetapSenior extends Karyawan implements BisaLembur, BisaBonus {
public KaryawanTetapSenior(String nama, double gajiBasis) {
super(nama, gajiBasis);
}
@Override
double hitungGaji() { return gajiBasis; }
@Override
public double hitungLembur(int jamLembur) {
return (gajiBasis / 173) * 1.5 * jamLembur;
}
@Override
public double hitungBonus(double persentase) {
return gajiBasis * (persentase / 100);
}
}
Kapan Menggunakan Interface #
Gunakan INTERFACE jika:
✓ Kamu mendefinisikan kemampuan yang bisa dimiliki kelas yang tidak berkaitan
(Terbang bisa dimiliki Burung dan Pesawat — dua hal yang tidak related)
✓ Kamu butuh multiple inheritance
✓ Kamu ingin API yang bisa diimplementasikan pihak lain (library, plugin)
✓ Tidak ada state bersama yang perlu dibawa
Gunakan KELAS ABSTRAK jika:
✓ Ada hubungan "adalah" yang jelas dalam hierarki
✓ Subkelas butuh atribut atau konstruktor dari kelas induk
✓ Ada implementasi default yang butuh akses ke state (atribut instance)
✓ Kamu ingin metode dengan access modifier selain public
Gunakan KEDUANYA jika:
✓ Kelas abstrak mendefinisikan hierarki, interface menambah kemampuan lintas hierarki
✓ Contoh: abstract class Karyawan + interface BisaLembur, BisaBonus
Ringkasan #
- Interface adalah kontrak — mendefinisikan apa yang harus bisa dilakukan, bukan bagaimana caranya. Kelas yang
implementswajib menyediakan semua metode abstraknya.- Semua metode interface secara default
public abstract— tidak perlu ditulis eksplisit. Field di interface otomatispublic static final(konstanta).- Interface bisa dipakai sebagai tipe data — ini fondasi polimorfisme. Kode yang ditulis terhadap interface bisa bekerja dengan semua kelas yang mengimplementasikannya, termasuk kelas yang belum ada saat ini.
- Satu kelas bisa implement banyak interface — ini cara Java mencapai multiple inheritance tanpa ambiguitas. Gunakan untuk menambah kemampuan lintas hierarki kelas.
default method(Java 8+) memungkinkan evolusi API — tambahkan metode baru ke interface tanpa merusak semua implementor lama. Implementor bisa override jika perlu, atau pakai implementasi default.static methoddi interface adalah utilitas — tidak diwarisi ke implementor, dipanggil lewat nama interface (NamaInterface.namaMetode()).- Interface vs kelas abstrak — pilih interface untuk kemampuan (“bisa terbang”), pilih kelas abstrak untuk hierarki (“adalah karyawan”). Keduanya bisa dipakai bersamaan.
- Java standard library dibangun di atas interface —
Comparable,Runnable,Iterable,Closeableadalah contoh nyata. Implementasikan interface yang relevan agar kode kamu bisa bekerja mulus dengan ekosistem Java.