Interface adalah tipe yang hanya menetapkan kontrak metode tanpa menyimpan keadaan, sehingga kelas mana pun yang menyediakan isinya dapat dipakai secara bergantian di tempat kontrak itu dituntut. Topik ini memandu deklarasi interface pertama, penerapannya di beberapa kelas yang tidak memiliki hubungan pewarisan, serta pemakaian tipe interface untuk variabel, parameter, dan elemen array. Setelah contoh dijalankan, pergantian implementasi cukup dilakukan di satu titik, yaitu saat objek dibuat, sementara seluruh kode pemakainya tetap utuh.
Mendeklarasikan interface#
Interface dideklarasikan dengan kata kunci interface, dan metodenya ditulis tanpa isi seperti metode abstrak, tetapi tanpa kata kunci abstract. Java memperlakukan metode di dalam interface sebagai public abstract secara implisit, sehingga setiap kelas yang mengimplementasikannya wajib menyediakan isinya. Kelas mengikat dirinya pada kontrak lewat kata kunci implements, dan satu kelas boleh mengimplementasikan lebih dari satu interface. Kode berikut menetapkan kontrak pengukuran bernama Pengukur beserta dua kelas sensor yang tidak saling berhubungan.
public class DemoSensor {
public static void main(String[] args) {
SensorSuhu suhu = new SensorSuhu("Gudang A", 27.6);
SensorKelembapan lembap = new SensorKelembapan("Gudang A", 64.0);
System.out.println(suhu.nama() + ": " + suhu.baca());
System.out.println(lembap.nama() + ": " + lembap.baca());
}
}
interface Pengukur {
String nama();
double baca();
}
class SensorSuhu implements Pengukur {
String lokasi;
double celsius;
SensorSuhu(String lokasi, double celsius) {
this.lokasi = lokasi;
this.celsius = celsius;
}
public String nama() {
return "sensor suhu " + lokasi;
}
public double baca() {
return celsius;
}
}
class SensorKelembapan implements Pengukur {
String lokasi;
double persen;
SensorKelembapan(String lokasi, double persen) {
this.lokasi = lokasi;
this.persen = persen;
}
public String nama() {
return "sensor kelembapan " + lokasi;
}
public double baca() {
return persen;
}
}sensor suhu Gudang A: 27.6
sensor kelembapan Gudang A: 64.0Menjalankan programnya dari terminal cukup dengan satu perintah berikut.
# berada di folder tempat DemoSensor.java disimpan
java DemoSensor.javasensor suhu Gudang A: 27.6
sensor kelembapan Gudang A: 64.0Kelas SensorSuhu dan SensorKelembapan tidak punya hubungan apa pun selain menepati kontrak yang sama. Perhatikan bahwa implementasi nama() dan baca() pada keduanya selalu diberi public. Metode interface secara implisit bersifat publik, sehingga implementasi dengan akses lebih sempit ditolak kompiler dengan pesan attempting to assign weaker access privileges; was public. Penulisan public secara eksplisit memang lazim ditinggalkan di dalam interface, tetapi wajib dituliskan pada saat metode diisi di kelasnya.
Polimorfisme lewat tipe kontrak#
Tipe interface berperan seperti tipe induk pada polimorfisme, yaitu variabel, parameter, dan elemen array bertipe kontrak boleh menampung objek dari kelas pengimplementasi mana pun. Dynamic dispatch yang berlaku pada kelas juga bekerja sama persis di sini, yaitu metode yang berjalan dipilih menurut tipe objeknya saat program berjalan, bukan menurut tipe variabelnya. Program berikut menyusun laporan dari tiga sensor berbeda jenis tanpa satu pun percabangan yang memeriksa jenisnya.
public class LaporanPengukuran {
public static void main(String[] args) {
Pengukur[] deret = {
new SensorSuhu("Kandang", 29.4),
new SensorKelembapan("Kandang", 71.5),
new SensorSuhu("Kantor", 24.8)
};
for (Pengukur p : deret) {
System.out.println(p.nama() + " membaca " + p.baca());
}
System.out.printf("Rata-rata: %.2f%n", rataRata(deret));
}
static double rataRata(Pengukur[] pengukur) {
double total = 0;
for (Pengukur p : pengukur) {
total = total + p.baca();
}
return total / pengukur.length;
}
}
interface Pengukur {
String nama();
double baca();
}
class SensorSuhu implements Pengukur {
String lokasi;
double celsius;
SensorSuhu(String lokasi, double celsius) {
this.lokasi = lokasi;
this.celsius = celsius;
}
public String nama() {
return "sensor suhu " + lokasi;
}
public double baca() {
return celsius;
}
}
class SensorKelembapan implements Pengukur {
String lokasi;
double persen;
SensorKelembapan(String lokasi, double persen) {
this.lokasi = lokasi;
this.persen = persen;
}
public String nama() {
return "sensor kelembapan " + lokasi;
}
public double baca() {
return persen;
}
}sensor suhu Kandang membaca 29.4
sensor kelembapan Kandang membaca 71.5
sensor suhu Kantor membaca 24.8
Rata-rata: 41.90Metode rataRata hanya bergantung pada janji baca() milik Pengukur, sehingga menambah jenis sensor baru, misalnya sensor tekanan udara, tidak menyentuh metode itu sama sekali. Cukup kelas baru yang dideklarasikan implements Pengukur, dan seluruh kode pemakai langsung mengenalinya. Hubungan kontrak dengan pengimplementasinya digambarkan berikut ini.
Banyak interface pada satu kelas#
Satu kelas boleh mengimplementasikan banyak interface sekaligus, dan di titik ini interface melampaui kelas abstrak yang hanya dapat diwarisi satu. Kemampuan-kemampuan kecil yang menyeberang hierarki kelas, misalnya kemampuan dinyalakan atau dijadwalkan, paling alami dimodelkan sebagai kontrak terpisah, lalu dilekatkan ke kelas yang memang membutuhkannya. Contoh berikut memberi dua kemampuan kepada satu kelas PompaAir.
public class UjiBanyakInterface {
public static void main(String[] args) {
PompaAir pompa = new PompaAir();
nyalakan(pompa);
pompa.mati();
cetakJadwal(pompa, 30);
}
static void nyalakan(Saklar saklar) {
saklar.nyala();
}
static void cetakJadwal(Terjadwal terjadwal, int menit) {
System.out.println("Menyala tiap " + menit + " menit, durasi "
+ terjadwal.durasiDetik() + " detik.");
}
}
interface Saklar {
void nyala();
void mati();
}
interface Terjadwal {
int durasiDetik();
}
class PompaAir implements Saklar, Terjadwal {
public void nyala() {
System.out.println("Pompa air menyala.");
}
public void mati() {
System.out.println("Pompa air berhenti.");
}
public int durasiDetik() {
return 120;
}
}Pompa air menyala.
Pompa air berhenti.
Menyala tiap 30 menit, durasi 120 detik.Objek pompa dapat dikirim ke metode nyalakan karena memenuhi kontrak Saklar, dan dapat dikirim ke cetakJadwal karena memenuhi kontrak Terjadwal, tanpa kedua metode itu perlu saling mengenal. Pewarisan ganda pada interface diperbolehkan karena kontraknya tidak membawa field instance, sehingga tidak ada konflik keadaan yang harus diwaspadai. Konflik baru mungkin muncul bila kedua interface sama-sama membawa metode dengan isi, dan aturan penyelesaiannya dibahas pada topik metode default dan static.
Memilih interface atau kelas abstrak#
Keduanya sama-sama mendefinisikan kontrak, sehingga pilihannya jatuh pada apa yang ikut diwariskan. Interface dipilih bila yang dibagi hanya kemampuan tanpa keadaan, terutama bila kemampuan itu harus dilekatkan pada kelas-kelas yang tidak seharusnya satu hierarki. Kelas abstrak dipilih bila turunannya berbagi field dan sebagian implementasi, sebagaimana kelas Bentuk yang membawa nama bersama bagi semua bentuknya. Banyak tipe inti di pustaka standar Java berbentuk interface, misalnya List untuk daftar, Map untuk pasangan kunci-nilai, Comparable untuk pembandingan, dan Runnable untuk tugas yang dijalankan thread. Semuanya menegaskan pola yang sama, yaitu pemakai berhenti pada tipe kontraknya sedangkan detail implementasinya bebas berganti.
Kesalahan umum#
- Melupakan
publicpada implementasi metode interface. Metode di interface implisit publik, sehingga implementasi tanpapublicditolak kompiler dengan pesanattempting to assign weaker access privileges. Beripublicpada setiap metode yang mengisi kontrak. - Mencoba membuat objek dari interface. Pernyataan
new Pengukur()ditolak dengan pesanPengukur is abstract; cannot be instantiated, karena interface tidak punya konstruktor dan tidak menyimpan keadaan. Yang dibuat adalah objek kelas pengimplementasinya. - Menyisakan satu metode kontrak yang belum diisi. Kelas yang belum melengkapi seluruh metode abstrak ditolak dengan pesan
is not abstract and does not override abstract method. Lengkapi metodenya, atau nyatakan kelas ituabstractjuga. - Mengira variabel bertipe interface mengenal metode kelasnya. Variabel
Pengukur phanya dapat memanggilnama()danbaca(), kendati objeknya sebenarnyaSensorSuhu. Metode khas kelasnya baru dapat dipanggil setelah downcast, sebagaimana pada polimorfisme kelas.
Ringkasan#
- Interface menetapkan kontrak metode tanpa keadaan, dan kelas menepatinya lewat
implementsdengan mengisi seluruh metodenya sebagaipublic. - Variabel, parameter, dan elemen array bertipe interface menerima objek kelas pengimplementasi mana pun, sehingga dynamic dispatch bekerja seperti pada tipe induk.
- Satu kelas boleh mengimplementasikan banyak interface karena kontraknya tidak membawa field instance, sehingga tidak ada konflik keadaan.
- Interface dipilih untuk kemampuan yang menyeberang hierarki kelas, sedangkan kelas abstrak dipilih bila keadaan dan kode bersama ikut diwariskan.
Referensi#
- The Java Tutorials: Defining an Interface — Oracle — Aturan deklarasi interface beserta pemodifikasi implisit metode dan konstantanya.
- The Java Tutorials: Implementing an Interface — Oracle — Cara kelas menepati kontrak interface dan syarat aksesnya.
- JLS SE 21 §9.1: Interface Declarations — Oracle — Spesifikasi resmi deklarasi interface dan anggotanya.