Kelas abstrak (abstract class) adalah kelas yang diperuntukkan menjadi induk, bukan bahan pembuatan objek, dan boleh memuat metode abstrak yang isinya dititipkan kepada turunan. Kombinasi ini menghasilkan kontrak parsial, yaitu sebagian perilaku ditetapkan tanpa isi dan sebagian lainnya sudah disediakan induk. Topik ini menunjukkan kapan kelas biasa tak lagi memadai, cara mendeklarasikannya, dan kapan pilihan jatuh ke interface.
Kapan kelas biasa tak lagi memadai#
Kelas dasar yang masih konkret menyimpan godaan berbahaya, yaitu perilaku default yang tidak bermakna. Contoh pada topik polimorfisme memuat luas() yang mengembalikan 0 di kelas Bentuk, nilai yang hanya berfungsi membuat kode lolos kompilasi. Turunan yang lupa mengoverride tidak tertangkap kompiler, karena 0 terasa sah, dan hasil hitungnya merusak program secara diam-diam. Kelas seperti ini sebenarnya tidak pernah dijadikan objek secara waras, karena new Bentuk() tidak mewakili keadaan apa pun.
Situasi itu adalah sinyal desain, bukan kebetulan. Bila sebuah kelas hanya berdiri untuk diperluas, janji-janjinya sebaiknya ditegakkan pada turunan, bukan ditutupi nilai kosong. Java menyediakan mekanisme itu lewat kata kunci abstract, yang membuat metode tanpa isi wajib diimplementasikan turunan dan membuat kelasnya tidak dapat diinstansiasi. Kontrak menjadi ketat di sisi yang memang harus berbeda, sementara kode yang sama tetap ditulis sekali di induk.
Mendeklarasikan kelas dan metode abstrak#
Kata kunci abstract dipasang di depan kelas untuk melarang instansiasi, dan di depan metode untuk menyerahkan isinya kepada turunan. Metode abstrak diakhiri titik koma tanpa badan, dan setiap turunan konkret wajib mengimplementasikan seluruh metode abstrak yang diwarisinya. Di sisi lain, kelas abstrak tetap boleh memuat field, konstruktor, dan metode konkret, karena turunannya memang butuh fondasi itu.
public class DemoKelasAbstrak {
public static void main(String[] args) {
Bentuk[] daftar = {
new Lingkaran(7),
new Persegi(5)
};
for (Bentuk b : daftar) {
System.out.println(b.deskripsi());
}
}
}
abstract class Bentuk {
String nama;
Bentuk(String nama) {
this.nama = nama;
}
abstract double luas();
String deskripsi() {
return nama + " seluas " + luas();
}
}
class Lingkaran extends Bentuk {
double jariJari;
Lingkaran(double jariJari) {
super("lingkaran");
this.jariJari = jariJari;
}
@Override
double luas() {
return Math.PI * jariJari * jariJari;
}
}
class Persegi extends Bentuk {
double sisi;
Persegi(double sisi) {
super("persegi");
this.sisi = sisi;
}
@Override
double luas() {
return sisi * sisi;
}
}lingkaran seluas 153.93804002589985
persegi seluas 25.0Metode luas() kini dinyatakan tanpa isi, sehingga Lingkaran dan Persegi wajib menyediakan rumusnya. Kelalaian itu tidak lagi tertutup nilai 0, melainkan berhenti di layar kompilasi dengan pesan class Lingkaran is not abstract and does not override abstract method luas() in Bentuk. Sementara itu, deskripsi() tetap ditulis sekali di induk dan memanggil luas() tanpa peduli rumus siapa yang berjalan. Dengan begitu, bagian yang sama kini berada di induk dan bagian yang berbeda berada di turunannya masing-masing.
Objek kelas abstrak tidak dapat dibuat#
Kelas abstrak melarang new secara langsung, dan larangan itu justru nilai jualnya. Tidak ada lagi objek setengah jadi yang berkeliling memakai metode yang seharusnya diimplementasikan turunan. Variabel bertipe kelas abstrak tetap sah dan justru lazim, karena yang disimpan adalah objek turunannya, sedangkan larangan hanya berlaku pada pembuatan objek dari kelas itu sendiri.
// tidak-dijalankan
Bentuk b = new Bentuk("bentuk");
// error: Bentuk is abstract; cannot be instantiatedTurunan yang belum siap mengisi seluruh kontrak pun masih punya jalan, yaitu menyatakan dirinya abstrak juga dan menyisakan sebagian metode abstrak kepada turunan berikutnya. Rantai ini memungkinkan hierarki bertingkat, misalnya kelas menengah yang menyediakan sebagian perilaku umum namun masih menunggu detail dari turunan paling bawah.
Kontrak parsial dengan metode konkret di induk#
Kekuatan sebenarnya muncul ketika metode konkret di induk menyusun langkah-langkah yang tetap, sementara langkah yang berbeda tiap turunan dinyatakan abstrak. Pola ini dikenal sebagai pola template method, yaitu kerangka proses dipatok di induk dan detailnya diisi turunan. Contoh berikut mematok urutan ekspor laporan, yaitu header lalu isi, dengan isi diserahkan kepada tiap bentuk ekspor.
public class UjiKontrakParsial {
public static void main(String[] args) {
EksporTeks ekspor = new EksporTeks("Rata-rata ujian", 82.5);
System.out.println(ekspor.ekspor());
}
}
abstract class EksporLaporan {
String judul;
double nilai;
EksporLaporan(String judul, double nilai) {
this.judul = judul;
this.nilai = nilai;
}
String ekspor() {
return header() + "\n" + isi();
}
String header() {
return "=== " + judul + " ===";
}
abstract String isi();
}
class EksporTeks extends EksporLaporan {
EksporTeks(String judul, double nilai) {
super(judul, nilai);
}
@Override
String isi() {
return "Nilai " + judul + ": " + nilai;
}
}=== Rata-rata ujian ===
Nilai Rata-rata ujian: 82.5Metode ekspor() konkret menjamin urutan kerjanya sama untuk semua turunan, sedangkan isi() abstrak menjamin setiap turunan menyediakan bagian yang berbeda. Bila kelak ada EksporCsv, satu metode isi() saja yang ditulis dan urutannya otomatis mengikuti induk. Kontrak semacam ini sulit ditegakkan dengan kelas biasa, karena tidak ada cara memaksa turunan menyediakan metode tertentu tanpa menyembunyikan default yang meragukan.
Kelas abstrak dan interface#
Kelas abstrak dan interface sama-sama mendefinisikan kontrak, dan perbedaannya terletak pada apa yang boleh dibawa. Kelas abstrak membawa keadaan, yaitu field dan konstruktor, serta dapat diwariskan hanya oleh satu kelas. Interface mengatur perilaku tanpa keadaan, dan satu kelas boleh mengimplementasikan banyak interface sekaligus.
| Aspek | Kelas abstrak | Interface |
|---|---|---|
| Field instance | boleh, lengkap dengan konstruktor | hanya konstanta |
| Warisan | satu kelas induk saja | banyak interface sekaligus |
| Metode konkret | boleh, untuk kode yang dibagi | boleh, sebagai default |
| Paling pas untuk | berbagi keadaan dan kode antarturunan | kemampuan yang dilekatkan lintas kelas |
Pilihan praktisnya sederhana. Bila turunan-turunannya berbagi data dan sebagian implementasi, kelas abstrak adalah tempatnya, sebagaimana Bentuk membawa nama dan deskripsi(). Bila yang dibagi hanya kemampuan tanpa keadaan, misalnya kemampuan dibandingkan atau dicetak, interface lebih ringan dan tidak mengunci satu-satunya jatah pewarisan kelas.
Kesalahan umum#
- Mengira kelas abstrak tidak boleh punya konstruktor. Konstruktor tetap diperlukan, karena konstruktor turunan memanggilnya lewat
super(...)untuk mengisi field induk. Yang dilarang adalahnewterhadap kelas abstraknya sendiri. - Turunan tidak mengimplementasikan semua metode abstrak. Kompiler menolak dengan pesan
is not abstract and does not override abstract method, dan perbaikannya dua arah, yaitu mengisi metodenya atau menyatakan turunan itu abstrak juga. - Memasang
abstractpada metodestaticatauprivate. Keduanya tidak dapat dioverride, sehingga kontraknya mustahil dipenuhi turunan, dan kompiler menolaknya. Metode abstrak harus bisa dicapai dan diganti oleh turunan. - Membuat kelas abstrak untuk satu turunan saja. Abstraksi yang tidak pernah dibagi adalah beban tambahan tanpa manfaat. Tahan diri hingga turunan kedua benar-benar muncul dan bagian bersamanya terlihat.
Ringkasan#
- Kelas abstrak tidak dapat diinstansiasi dan diperuntukkan menjadi induk yang membawa field, konstruktor, serta metode konkret.
- Metode abstrak dinyatakan tanpa isi dan wajib diimplementasikan turunan konkret, sehingga kelalaian berhenti di kompilasi.
- Pola template method mematok kerangka proses di induk sementara langkah yang berbeda diserahkan kepada turunan.
- Kelas abstrak dipilih bila turunan berbagi keadaan, sedangkan interface dipilih untuk kemampuan tanpa keadaan yang menyeberang hierarki.
Referensi#
- The Java Tutorials: Abstract Methods and Classes — Oracle — Panduan resmi deklarasi kelas abstrak, metode abstrak, dan hierarki bertingkatnya.
- JLS SE 21 §8.1.1.1: abstract Classes — Oracle — Spesifikasi kelas abstrak beserta larangan instansiasinya.
- JLS SE 21 §8.4.3.1: abstract Methods — Oracle — Aturan metode abstrak dan kewajiban kelas turunannya.