Pewarisan merupakan salah satu ujung-tombak pemrograman berorientasi-objek karena mekanisme ini dapat dipakai untuk menciptakan klasifikasi-klasifikasi hierarkis. Dengan menggunakan pewarisan, Anda dapat menciptakan sebuah kelas umum yang mendefinisikan watak bersama dari sejumlah entitas yang berelasi. Kelas ini kemudian dapat diwarisi oleh kelas-kelas yang lebih spesifik, dengan menambahkan watak unik pada kelas masing-masing. Dalam terminologi Java, sebuah kelas yang diwarisi disebut dengan superkelas. Kelas yang mewarisi disebut dengan subkelas. Oleh karena itu, sebuah subkelas merupakan versi spesifik dari superkelas. Subkelas mewarisi semua anggota yang didefinisikan oleh superkelas dan menambahkan elemen-elemen uniknya sendiri.
Dasar Pewarisan
Untuk mewarisi sebuah kelas, Anda hanya perlu menambahkan definisi dari satu kelas menggunakan katakunci extends. Untuk melihat bagaimana hal ini dilakukan, Anda akan melihat sebuah program pendek berikut. Program berikut menciptakan sebuah superkelas dengan nama A dan sebuah subkelas dengan nama B. Perhatikan bagaimana katakunci extends dipakai untuk menciptakan subkelas A.
Keluaran program ditampilkan berikut:
Seperti yang dapat Anda lihat, subkelas B mencakup semua anggota dari superkelasnya, A. Inilah mengapa subOb dapat mengakses i dan j dan memanggil tampilij(). Selain itu, di dalam jum(), i dan j dapat diakses secara langsung, layaknya keduanya bagian dari subkelas B.
Meskipun A merupakan superkelas bagi B, A juga merupakan kelas yang independen dan berdiri sendiri. Menjadi superkelas tidak berarti ia tidak bisa digunakan. Inilah mengapa superOb dapat menetapkan nilai untuk i dan j dan mengakses metode tampilij().
Bentuk umum dari deklarasi kelas yang mewarisi sebuah superkelas ditunjukkan di sini:
Anda hanya bisa menetapkan satu superkelas untuk sembarang subkelas yang Anda ciptakan. Java tidak mendukung keberadaan pewarisan lebih dari satu superkelas.
Akses Anggota dan Pewarisan
Meskipun subkelas dapat mengakses anggota-anggota dari superkelasnya, ia tidak bisa mengakses anggota-anggota superkelas yang dideklarasikan private. Sebagai contoh, perhatikan hierarki kelas sederhana berikut:
Program ini tidak bisa dikompilasi karena penggunaan j di dalam metode jum() dari B menyebabkan pelanggaran akses. Karena j dideklarasikan private, ia hanya bisa diakses oleh anggota-anggota dari sesama kelasnya sendiri. Subkelas tidak bisa mengaksesnya.
Contoh Yang Lebih Praktis
Anda akan melihat contoh lebih praktis yang akan membantu mengilustrasikan kekuatan pewarisan. Di sini, versi terakhir dari kelas Kotak dikembangkan pada Bab 6 akan diperluas untuk melibatkan komponen keempat dengan nama berat. Jadi, kelas baru ini akan memuat lebar, tinggi, dalam, dan berat kotak.
Ketika dijalankan, program ini menghasilkan keluaran:
BeratKotak mewarisi semua karakteristik dari Kotak dan menambahkan sendiri komponen berat. Tidak diperlukan bagi BeratKotak untuk menciptakan sendiri fitur yang sudah ada pada superkelasnya, ia hanya perlu mewarisinya dari Kotak.
Keuntungan utama dari pewarisan adalah setelah Anda menciptakan superkelas yang mendefinisikan watak umum pada sekumpulan objek, superkelas tersebut dapat dipakai untuk menciptakan sejumlah subkelas dengan watak yang lebih spesifik. Pada contoh berikut, kelas berikut mewarisi Kotak dan menambahkan data warnanya sendiri:
Ketika dijalankan, program ini menghasilkan keluaran:
Variabel Superkelas Dapat Mengakses Objek Subkelas
Variabel referensi dari superkelas dapat ditugasi referensi ke setiap subkelas yang diderivasi dari superkelas tersebut. Anda akan menjumpai aspek pewarisan berikut berguna pada banyak situasi. Sebagai contoh, perhatikan berikut:
Di sini, beratkotak merupakan sebuah referensi ke objek-objek BeratKotak, dan kotaksederhana adalah referensi ke objek-objek Kotak. Karena BeratKotak adalah subkelas dari Kotak, maka adalah hal valid untuk menugaskan referensi ke objek beratkotak kepada referensi kotaksederhana.
Menggunakan super
Pada contoh-contoh sebelumnya, kelas-kelas yang diderivasi dari Kotak tidak diimplementasikan seefisien atau sehandal yang seharusnya. Misalnya, konstruktor untuk BeratKotak secara eksplisit menginisialisasi bidang lebar, tinggi, dan dalam. Ini menyebabkan penduplikasian kode pada superkelas, sebuah pekerjaan yang tidak efisien.
Adakalanya ketika Anda ingin menciptakan superkelas yang menyimpan detil implementasinya untuk dirinya sendiri (yaitu, membuat anggota datanya dideklarasikan private). Pada kasus tersebut, tidak ada cara lain bagi subkelas selain menginisialisasi varabel-variabelnya sendiri. Karena enkapsulai merupakan fitur utama dalam PBO, tidaklah mengherankan apabila Java memberikan solusi untuk permasalahan ini. Ketika subkelas perlu mengakses anggota dari superkelas, ia dapat melakukannya menggunakan katakunci super.
Katakunci super berguna dalam dua hal. Yang pertama, ia dapat memanggil konstruktor dari superkelas. Yang kedua, ia bisa dipakai untuk mengakses anggota superkelas yang disembunyikan dari anggota subkelasnya. Masing-masing kegunaan ini akan diuji di sini.
Menggunakan super untuk Memanggil Konstruktor Superkelas
Subkelas dapat memanggil konstruktor yang didefinisikan oleh superkelasnya menggunakan bentuk super berikut:
Di sini, daftar-argumen menetapkan argumen-argumen yang diperlukan oleh konstruktor pada superkelas. Ingat bahwa super() harus merupakan statemen pertama yang dieksekusi di dalam konstruktor subkelas.
Untuk melihat bagaimana super() digunakan, perhatikan versi terperbaiki dari kelas BeratKotak berikut. Perhatikan bahwa bidang lebar, tinggi, dan dalam pada superkelas telah dideklarasikan private:
Ketika dijalankan, program ini menghasilkan keluaran:
Penggunaan Kedua dari super
Bentuk kedua dari super berperilaki mirip dengan this, tetapi super mereferensi superkelas dari subkelas dimana katakunci ini digunakan. Penggunaan kedua ini memiliki bentuk umum:
Di sini, anggota dapat berupa metode atau variabel kelas.
Bentuk kedua dari super paling banyak diterapkan pada situasi dimana anggota dari subkelas memiliki nama sama dengan anggota kelas pada superkelas. Perhatikan contoh sederhana ini:
Jika dijalankan, program ini menghasilkan keluaran berikut:
Meskipun variabel kelas i pada B menyembunyikan variabel i pada A, super dapat dipakai untuk mengakses i yang didefinisikan pada superkelas. Seperti pada program berikut, super juga dapat dipakai untuk memanggil metode yang tersembunyi oleh subkelas.
Jika dijalankan, program ini menghasilkan keluaran berikut:
Meskipun metode kelas tampil() pada B menyembunyikan metode kelas tampil() pada A, super dapat dipakai untuk mengakses tampil() yang didefinisikan pada superkelas.
Menciptakan Hierarki Pewarisan Multilevel
Sampai titik ini, Anda telah menggunakan sejumlah hierarki pewarisan kelas sederhana yang hanya memuat satu superkelas dan satu subkelas. Namun, Anda bisa membangun hierarki pewarisan yang memuat sebanyak mungkin lapisan pewarisan. Seperti disebutkan sebelumnya, Anda bisa menggunakan subkelas dari sebuah superkelas agar subkelas tersebut menjadi superkelasi bagi kelas lainnya. Misalnya, jika diberikan tiga kelas A, B, dan C. Kelas C dapat menjadi subkelas dari B, dan B menjadi subkelas dari A. Ketika kasus ini terjadi, setiap subkelas mewarisi semua karakteristik dari semua superkelasnya. Jadi, C mewarisi semua aspek dari B dan C. Untuk melihat bagaimana hierarki multilevel ini digunakan, perhatikan program berikut. Pada program tersebut, subkelas BeratKotak dipakai sebagai superkelas dari kelas Pengiriman. Kelas Pengiriman mewarisi semua karakteristik (data dan metode) dari BeratKotak dan Kotak. Kelas Pengiriman juga menambahkan sebuah bidang dengan nama biaya, yang memuat biaya pengiriman.
Program ini menghasilkan keluaran berikut:
Karena pewarisan, Pengiriman dapat menggunakan kelas Kotak dan BeratKotak, dan menambahkan informasi ekstra yang berkaitan dengan biaya pengiriman.
Contoh ini mengilustrasikan satu hal penting lain: super() selalu merujuk ke konstruktor superkelas terdekat. Jadi, super() pada BeratKotak memanggil konstruktor pada Kotak dan super() pada Pengiriman selalu memanggil konstruktor pada BeratKotak.
Kapan Konstruktor Dieksekusi
Ketika sebuah hierarki pewarisan diciptakan, bagaimanakah urutan eksekusi konstruktor untuk kelas-kelas yang membangun hierarki tersebut? Misalnya, jika diberikan subkelas B dan superkelas A, apakah konstruktor pada kelas A dieksekusi sebelum konstruktor kelas B, atau sebaliknya? Jawabannya adalah bahwa pada hierarki pewarisan, konstruktor pada superkelas dieksekusi lebih dahulu baru kemudian konstruktor pada subkelas dieksekusi. Program berikut mengilustrasikan kapan konstruktor-konstruktor dieksekusi:
Jika dijalankan, program ini menghasilkan keluaran:
Seperti yang Anda lihat, konstruktor dieksekusi sesuai dengan rantai pewarisan, mulai dari konstruktor pada superkelas sampai konstruktor subkelas terbawah.
Jika super() dilibatkan dalam mekanisme pewarisan? Bagaimanakah urutan eksekusi konstruktor defaultnya? Program berikut membuktikan bahwa meskipun super() dilibatkan dalam pewarisan kelas, urutan eksekusi konstruktor tetap mulai dari konstruktor superkelas sampai konstruktor subkelas terbawah:
Jika dijalankan, program ini menghasilkan keluaran:
Hal yang sama berlaku untuk konstruktor terparameterisasi atau konstruktor berparameter. Meskipun super() dilibatkan dalam mekanisme pewarisan, urutan eksekusi konstruktor tetap mulai dari konstruktor superkelas sampai konstruktor subkelas terbawah. Hal ini diilustrasikan pada program berikut ini:
Jika dijalankan, program ini menghasilkan keluaran:
Mendefinisikan-Ulang (Overriding) Metode
Para hierarki pewarisan kelas, ketika sebuah metode pada subkelas memiliki nama sama dan sidik tipe data (tipe data parameter dan jumlah parameter) sama seperti pada superkelasnya, maka metode pada subkelas tersebut dikatakan mendefinisikan ulang (override) metode pada superkelas. Ketika metode pada superkelas tersebut dipanggil di dalam subkelasnya, maka yang dipanggil adalah metode yang didefinisikan ulang oleh subkelas. Perhatikan program berikut:
Ketika dijalankan, program di atas menghasilkan keluaran:
Ketika tampil() dipanggil pada sebuah objek bertipe B, maka versi tampil() yang didefinisikan di dalam B yang digunakan. Jadi, versi dari tampil() di dalam B mendefinisikan ulang (membatalkan) versi tampil() yang didefinisikan di dalam A.
Jika Anda ingin mengakses versi superkelas dari metode yang dibatalkan, Anda dapat melakukannya menggunakan super. Misalnya, pada versi dari kelas B ini, versi superkelas dari tampil() dipanggil di dalam versi subkelas. Perhatikan program berikut:
Ketika dijalankan, program di atas menghasilkan keluaran:
Pendefinisian-ulang (pembatalan) metode hanya terjadi ketika nama dan sidik tipe data (tipe data parameter dan jumlah parameter) dari dua metode identik. Jika tidak, maka yang terjadi hanyalah pembebanan metode. Misalnya, perhatikan versi modifikasi dari contoh sebelumnya:
Ketika dijalankan, program di atas menghasilkan keluaran:
Menerapkan Pendefinisian-Ulang Metode
Perhatikan contoh praktis berikut yang menggunakan pendefinisian-ulang metode. Program berikut menciptakan sebuah superkelas Bangun yang menyimpan dimensi-dimensi dari sebuah objek dua-dimensi. Kelas itu juga mendefinisikan sebuah metode luas() yang menghitung luas objek. Program menderivasi dua subkelas dari Bangun. Subkelas pertama adalah PersegiPanjang dan yang kedua adalah SegiTiga. Setiap subkelas ini mendefinisikan-ulang metode luas() sehingga menghasilkan luas persegipanjang dan luas segitiga.
Ketika dijalankan, program di atas menghasilkan keluaran:
// Sebuah contoh pewarisan sederhana. // Menciptakan superkelas. class A { int i, j; void tampilij() { System.out.println("i dan j: " + i + " " + j); } } // Menciptakan subkelas yang mewarisi kelas A. class B extends A { int k; void tampilk() { System.out.println("k: " + k); } void jum() { System.out.println("i+j+k: " + (i+j+k)); } } public class PewarisanSederhana { public static void main(String args []) { A superOb = new A(); B subOb = new B(); // Superkelas sendiri bisa digunakan. superOb.i = 10; superOb.j = 20; System.out.println("Isi dari superOb: "); superOb.tampilij(); System.out.println(); // Subkelas bisa mengakses semua anggota publik dari superkelasnya subOb.i = 7; subOb.j = 8; subOb.k = 9; System.out.println("Isi dari subOb: "); subOb.tampilij(); subOb.tampilk(); System.out.println(); System.out.println("Penjumlahan atas i, j dan k pada subOb:"); subOb.jum(); } }
Keluaran program ditampilkan berikut:
Isi dari superOb: i dan j: 10 20 Isi dari subOb: i dan j: 7 8 k: 9 Penjumlahan atas i, j dan k pada subOb: i+j+k: 24
Seperti yang dapat Anda lihat, subkelas B mencakup semua anggota dari superkelasnya, A. Inilah mengapa subOb dapat mengakses i dan j dan memanggil tampilij(). Selain itu, di dalam jum(), i dan j dapat diakses secara langsung, layaknya keduanya bagian dari subkelas B.
Meskipun A merupakan superkelas bagi B, A juga merupakan kelas yang independen dan berdiri sendiri. Menjadi superkelas tidak berarti ia tidak bisa digunakan. Inilah mengapa superOb dapat menetapkan nilai untuk i dan j dan mengakses metode tampilij().
Bentuk umum dari deklarasi kelas yang mewarisi sebuah superkelas ditunjukkan di sini:
class nama-subkelas extends nama-superkelas { //tubuh kelas }
Anda hanya bisa menetapkan satu superkelas untuk sembarang subkelas yang Anda ciptakan. Java tidak mendukung keberadaan pewarisan lebih dari satu superkelas.
Akses Anggota dan Pewarisan
Meskipun subkelas dapat mengakses anggota-anggota dari superkelasnya, ia tidak bisa mengakses anggota-anggota superkelas yang dideklarasikan private. Sebagai contoh, perhatikan hierarki kelas sederhana berikut:
/* Pada sebuah hierarki kelas, anggota-anggota private * tetap bersifat lokal pada kelasnya. * Program ini memuat sebuah error dan tidak akan * bisa dikompilasi. */ // Menciptakan superkelas. class A { int i; // public secara default private int j; // private di dalam A void setij(int x, int y) { i = x; j = y; } } // j milai superkelas A tidak bisa diakses di sini. class B extends A { int total; void jum() { total = i + j; // ERROR, j tidak bisa diakses di sini } } public class AksesPewarisan { public static void main(String args[]) { B subOb = new B(); subOb.setij(10, 12); subOb.jum(); System.out.println("Total = " + subOb.total); } }
Program ini tidak bisa dikompilasi karena penggunaan j di dalam metode jum() dari B menyebabkan pelanggaran akses. Karena j dideklarasikan private, ia hanya bisa diakses oleh anggota-anggota dari sesama kelasnya sendiri. Subkelas tidak bisa mengaksesnya.
Contoh Yang Lebih Praktis
Anda akan melihat contoh lebih praktis yang akan membantu mengilustrasikan kekuatan pewarisan. Di sini, versi terakhir dari kelas Kotak dikembangkan pada Bab 6 akan diperluas untuk melibatkan komponen keempat dengan nama berat. Jadi, kelas baru ini akan memuat lebar, tinggi, dalam, dan berat kotak.
// Program ini menggunakan pewarisan // Superkelas class Kotak { double lebar; double tinggi; double dalam; /* Perhatikan konstruktor ini. Ia mengambil sebuah objek bertipe data Kotak.*/ Kotak(Kotak ob) { // melewatkan objek kepada konstruktor lebar = ob.lebar; tinggi = ob.tinggi; dalam = ob.dalam; } // konstruktor dipakai ketika semua dimensi diberikan Kotak(double w, double h, double d) { lebar = w; tinggi = h; dalam = d; } // konstruktir dipakai ketika tidak ada dimensi diberikan Kotak() { lebar = -1; // menggunakan -1 untuk mengindikasikan tinggi = -1; // sebuah kotak dalam = -1; // yang tidak diinisialisasi } // konstruktor digunakan ketika kubik diciptakan Kotak(double pjg) { lebar = tinggi = dalam = pjg; } // menghitung dan menghasilkan volume double volume() { return lebar * tinggi * dalam; } } //Di sini, Kotak diwarisi. class BeratKotak extends Kotak { double berat; // berat kotak // Konstruktor untuk BeratKotak BeratKotak(double w, double h, double d, double m) { lebar = w; tinggi = h; dalam = d; berat = m; } } public class DemoKotakBerat { public static void main(String args[]) { BeratKotak kotakKu1 = new BeratKotak(10, 20, 15, 34.3); BeratKotak kotakKu2 = new BeratKotak(2, 3, 4, 0.076); double vol; vol = kotakKu1.volume(); System.out.println("Volume dari kotakKu1 = " + vol); System.out.println("Berat dari kotakKu1 = " + kotakKu1.berat); System.out.println(); vol = kotakKu2.volume(); System.out.println("Volume dari kotakKu2 = " + vol); System.out.println("Berat dari kotakKu2 = " + kotakKu2.berat); } }
Ketika dijalankan, program ini menghasilkan keluaran:
Volume dari kotakKu1 = 3000.0 Berat dari kotakKu1 = 34.3 Volume dari kotakKu2 = 24.0 Berat dari kotakKu2 = 0.076
BeratKotak mewarisi semua karakteristik dari Kotak dan menambahkan sendiri komponen berat. Tidak diperlukan bagi BeratKotak untuk menciptakan sendiri fitur yang sudah ada pada superkelasnya, ia hanya perlu mewarisinya dari Kotak.
Keuntungan utama dari pewarisan adalah setelah Anda menciptakan superkelas yang mendefinisikan watak umum pada sekumpulan objek, superkelas tersebut dapat dipakai untuk menciptakan sejumlah subkelas dengan watak yang lebih spesifik. Pada contoh berikut, kelas berikut mewarisi Kotak dan menambahkan data warnanya sendiri:
// Program ini menggunakan pewarisan // Superkelas class Kotak { double lebar; double tinggi; double dalam; /* Perhatikan konstruktor ini. Ia mengambil sebuah objek bertipe data Kotak.*/ Kotak(Kotak ob) { // melewatkan objek kepada konstruktor lebar = ob.lebar; tinggi = ob.tinggi; dalam = ob.dalam; } // konstruktor dipakai ketika semua dimensi diberikan Kotak(double w, double h, double d) { lebar = w; tinggi = h; dalam = d; } // konstruktor dipakai ketika tidak ada dimensi diberikan Kotak() { lebar = -1; // menggunakan -1 untuk mengindikasikan tinggi = -1; // sebuah kotak dalam = -1; // yang tidak diinisialisasi } // konstruktor digunakan ketika kubik diciptakan Kotak(double pjg) { lebar = tinggi = dalam = pjg; } // menghitung dan menghasilkan volume double volume() { return lebar * tinggi * dalam; } } //Di sini, Kotak diwarisi. class BeratKotak extends Kotak { double berat; // berat kotak // Konstruktor untuk BeratKotak BeratKotak(double w, double h, double d, double m) { lebar = w; tinggi = h; dalam = d; berat = m; } } //Di sini, Kotak juga diwarisi dan menambahkan atribut warna class WarnaKotak extends Kotak { String warna; // warna kotak // Konstruktor untuk WarnaKotak WarnaKotak(double w, double h, double d, String c) { lebar = w; tinggi = h; dalam = d; warna = c; } } public class DemoKotakWarna { public static void main(String args[]) { //Menguji objek WarnaKotak WarnaKotak kotakKu1 = new WarnaKotak(10, 20, 15, "merah"); WarnaKotak kotakKu2 = new WarnaKotak(2, 3, 4, "kuning"); double vol; vol = kotakKu1.volume(); System.out.println("Volume dari kotakKu1 = " + vol); System.out.println("Warna dari kotakKu1 = " + kotakKu1.warna); System.out.println(); vol = kotakKu2.volume(); System.out.println("Volume dari kotakKu2 = " + vol); System.out.println("Warna dari kotakKu2 = " + kotakKu2.warna); } }
Ketika dijalankan, program ini menghasilkan keluaran:
Volume dari kotakKu1 = 3000.0 Warna dari kotakKu1 = merah Volume dari kotakKu2 = 24.0 Warna dari kotakKu2 = kuning
Variabel Superkelas Dapat Mengakses Objek Subkelas
Variabel referensi dari superkelas dapat ditugasi referensi ke setiap subkelas yang diderivasi dari superkelas tersebut. Anda akan menjumpai aspek pewarisan berikut berguna pada banyak situasi. Sebagai contoh, perhatikan berikut:
// Program ini menggunakan pewarisan // Superkelas class Kotak { double lebar; double tinggi; double dalam; /* Perhatikan konstruktor ini. Ia mengambil sebuah objek bertipe data Kotak.*/ Kotak(Kotak ob) { // melewatkan objek kepada konstruktor lebar = ob.lebar; tinggi = ob.tinggi; dalam = ob.dalam; } // konstruktor dipakai ketika semua dimensi diberikan Kotak(double w, double h, double d) { lebar = w; tinggi = h; dalam = d; } // konstruktor dipakai ketika tidak ada dimensi diberikan Kotak() { lebar = -1; // menggunakan -1 untuk mengindikasikan tinggi = -1; // sebuah kotak dalam = -1; // yang tidak diinisialisasi } // konstruktor digunakan ketika kubik diciptakan Kotak(double pjg) { lebar = tinggi = dalam = pjg; } // menghitung dan menghasilkan volume double volume() { return lebar * tinggi * dalam; } } //Di sini, Kotak diwarisi. class BeratKotak extends Kotak { double berat; // berat kotak // Konstruktor untuk BeratKotak BeratKotak(double w, double h, double d, double m) { lebar = w; tinggi = h; dalam = d; berat = m; } } public class DemoRef { public static void main(String args[]) { BeratKotak beratkotak = new BeratKotak(3, 5, 7, 8.37); Kotak kotaksederhana = new Kotak(); double vol; vol = beratkotak.volume(); System.out.println("Volume beratkotak = " + vol); System.out.println("Berat beratkotak = " + beratkotak.berat); System.out.println(); // menugaskan referensi BeratKotak kepada referensi Kotak kotaksederhana = beratkotak; vol = kotaksederhana.volume(); // OK, volume() didefinisikan di dalam Kotak System.out.println("Volume kotaksederhana = " + vol); /* Statemen berikut tak-valid karena kotaksederhana tidak mendefinisikan anggota berat. */ // System.out.println("Berat kotaksederhana = " + kotaksederhana.berat); } }
Di sini, beratkotak merupakan sebuah referensi ke objek-objek BeratKotak, dan kotaksederhana adalah referensi ke objek-objek Kotak. Karena BeratKotak adalah subkelas dari Kotak, maka adalah hal valid untuk menugaskan referensi ke objek beratkotak kepada referensi kotaksederhana.
Menggunakan super
Pada contoh-contoh sebelumnya, kelas-kelas yang diderivasi dari Kotak tidak diimplementasikan seefisien atau sehandal yang seharusnya. Misalnya, konstruktor untuk BeratKotak secara eksplisit menginisialisasi bidang lebar, tinggi, dan dalam. Ini menyebabkan penduplikasian kode pada superkelas, sebuah pekerjaan yang tidak efisien.
Adakalanya ketika Anda ingin menciptakan superkelas yang menyimpan detil implementasinya untuk dirinya sendiri (yaitu, membuat anggota datanya dideklarasikan private). Pada kasus tersebut, tidak ada cara lain bagi subkelas selain menginisialisasi varabel-variabelnya sendiri. Karena enkapsulai merupakan fitur utama dalam PBO, tidaklah mengherankan apabila Java memberikan solusi untuk permasalahan ini. Ketika subkelas perlu mengakses anggota dari superkelas, ia dapat melakukannya menggunakan katakunci super.
Katakunci super berguna dalam dua hal. Yang pertama, ia dapat memanggil konstruktor dari superkelas. Yang kedua, ia bisa dipakai untuk mengakses anggota superkelas yang disembunyikan dari anggota subkelasnya. Masing-masing kegunaan ini akan diuji di sini.
Menggunakan super untuk Memanggil Konstruktor Superkelas
Subkelas dapat memanggil konstruktor yang didefinisikan oleh superkelasnya menggunakan bentuk super berikut:
super(daftar-argumen);
Di sini, daftar-argumen menetapkan argumen-argumen yang diperlukan oleh konstruktor pada superkelas. Ingat bahwa super() harus merupakan statemen pertama yang dieksekusi di dalam konstruktor subkelas.
Untuk melihat bagaimana super() digunakan, perhatikan versi terperbaiki dari kelas BeratKotak berikut. Perhatikan bahwa bidang lebar, tinggi, dan dalam pada superkelas telah dideklarasikan private:
// Program ini mendemonstrasikan super() // Superkelas dengan tiga bidangnya dideklarasikan private class Kotak { private double lebar; private double tinggi; private double dalam; /* Perhatikan konstruktor ini. Ia mengambil sebuah objek bertipe data Kotak.*/ Kotak(Kotak ob) { // melewatkan objek kepada konstruktor lebar = ob.lebar; tinggi = ob.tinggi; dalam = ob.dalam; } // konstruktor dipakai ketika tiga-dimensi diberikan Kotak(double w, double h, double d) { lebar = w; tinggi = h; dalam = d; } // konstruktir dipakai ketika tidak ada dimensi diberikan Kotak() { lebar = -1; // menggunakan -1 untuk mengindikasikan tinggi = -1; // sebuah kotak dalam = -1; // yang tidak diinisialisasi } // konstruktor digunakan ketika kubik diciptakan Kotak(double pjg) { lebar = tinggi = dalam = pjg; } // menghitung dan menghasilkan volume double volume() { return lebar * tinggi * dalam; } } // BerakKotak sekarang mengimplementasikan semua konstruktor. class BeratKotak extends Kotak { double berat; // berat kotak // mengkonstruksi klon dari sebuah objek BeratKotak(BeratKotak ob) { // melewatkan objek kepada konstruktor super(ob); //memanggil konstruktor klon superkelas berat = ob.berat; } // konstruktor ketika semua parameter ditetapkan BeratKotak(double w, double h, double d, double m) { super(w, h, d); // memanggil konstruktor tiga-dimensi superkelas berat = m; } // konstruktor default BeratKotak() { super(); //memanggil konstruktor default dari superkelas berat = -1; } // konstruktor digunakan ketika kasus kubik BeratKotak(double pjg, double m) { super(pjg); //memanggil konstruktor kasus kubik dari superkelas berat = m; } } public class DemoSuper { public static void main(String args[]) { BeratKotak kotakKu1 = new BeratKotak(10, 20, 15, 34.3); BeratKotak kotakKu2 = new BeratKotak(2, 3, 4, 0.076); BeratKotak kotakKu3 = new BeratKotak(); // default BeratKotak kubikKu = new BeratKotak(3, 2); BeratKotak klonKu = new BeratKotak(kotakKu1); double vol; vol = kotakKu1.volume(); System.out.println("Volume dari kotakKu1 = " + vol); System.out.println("Berat dari kotakKu1 = " + kotakKu1.berat); System.out.println(); vol = kotakKu2.volume(); System.out.println("Volume dari kotakKu2 = " + vol); System.out.println("Berat dari kotakKu2 = " + kotakKu2.berat); System.out.println(); vol = kotakKu3.volume(); System.out.println("Volume dari kotakKu3 = " + vol); System.out.println("Berat dari kotakKu3 = " + kotakKu3.berat); System.out.println(); vol = klonKu.volume(); System.out.println("Volume dari klonKu = " + vol); System.out.println("Berat dari klonKu = " + klonKu.berat); System.out.println(); vol = kubikKu.volume(); System.out.println("Volume dari kubikKu = " + vol); System.out.println("Berat dari kubikKu = " + kubikKu.berat); System.out.println(); } }
Ketika dijalankan, program ini menghasilkan keluaran:
Volume dari kotakKu1 = 3000.0 Berat dari kotakKu1 = 34.3 Volume dari kotakKu2 = 24.0 Berat dari kotakKu2 = 0.076 Volume dari kotakKu3 = -1.0 Berat dari kotakKu3 = -1.0 Volume dari klonKu = 3000.0 Berat dari klonKu = 34.3 Volume dari kubikKu = 27.0 Berat dari kubikKu = 2.0
Penggunaan Kedua dari super
Bentuk kedua dari super berperilaki mirip dengan this, tetapi super mereferensi superkelas dari subkelas dimana katakunci ini digunakan. Penggunaan kedua ini memiliki bentuk umum:
super.anggota
Di sini, anggota dapat berupa metode atau variabel kelas.
Bentuk kedua dari super paling banyak diterapkan pada situasi dimana anggota dari subkelas memiliki nama sama dengan anggota kelas pada superkelas. Perhatikan contoh sederhana ini:
// Menggunakan super untuk mengatasi penyembunyian nama. class A { int i; } // Menciptakan subkelas dengan mewarisi kelas A. class B extends A { int i; // i ini menyembunyikan i pada A B(int a, int b) { super.i = a; // i pada A i = b; // i pada B } void tampil() { System.out.println("i pada superkelas: " + super.i); System.out.println("i pada subkelas: " + i); } } public class GunakanSuper { public static void main(String args[]) { B subOb = new B(1, 2); subOb.tampil(); } }
Jika dijalankan, program ini menghasilkan keluaran berikut:
i pada superkelas: 1 i pada subkelas: 2
Meskipun variabel kelas i pada B menyembunyikan variabel i pada A, super dapat dipakai untuk mengakses i yang didefinisikan pada superkelas. Seperti pada program berikut, super juga dapat dipakai untuk memanggil metode yang tersembunyi oleh subkelas.
// Menggunakan super untuk mengatasi penyembunyian nama. class A { int i; void tampil() { System.out.println("tampil() pada superkelas"); } } // Menciptakan subkelas dengan mewarisi kelas A. class B extends A { int i; // i ini menyembunyikan i pada A B(int a, int b) { super.i = a; // i pada A i = b; // i pada B } void tampil() { super.tampil(); System.out.println("tampil() pada subkelas"); } } public class SuperMetode { public static void main(String args[]) { B subOb = new B(1, 2); subOb.tampil(); } }
Jika dijalankan, program ini menghasilkan keluaran berikut:
tampil() pada superkelas tampil() pada subkelas
Meskipun metode kelas tampil() pada B menyembunyikan metode kelas tampil() pada A, super dapat dipakai untuk mengakses tampil() yang didefinisikan pada superkelas.
Menciptakan Hierarki Pewarisan Multilevel
Sampai titik ini, Anda telah menggunakan sejumlah hierarki pewarisan kelas sederhana yang hanya memuat satu superkelas dan satu subkelas. Namun, Anda bisa membangun hierarki pewarisan yang memuat sebanyak mungkin lapisan pewarisan. Seperti disebutkan sebelumnya, Anda bisa menggunakan subkelas dari sebuah superkelas agar subkelas tersebut menjadi superkelasi bagi kelas lainnya. Misalnya, jika diberikan tiga kelas A, B, dan C. Kelas C dapat menjadi subkelas dari B, dan B menjadi subkelas dari A. Ketika kasus ini terjadi, setiap subkelas mewarisi semua karakteristik dari semua superkelasnya. Jadi, C mewarisi semua aspek dari B dan C. Untuk melihat bagaimana hierarki multilevel ini digunakan, perhatikan program berikut. Pada program tersebut, subkelas BeratKotak dipakai sebagai superkelas dari kelas Pengiriman. Kelas Pengiriman mewarisi semua karakteristik (data dan metode) dari BeratKotak dan Kotak. Kelas Pengiriman juga menambahkan sebuah bidang dengan nama biaya, yang memuat biaya pengiriman.
// Program ini mendemonstrasikan hierarki pewarisan multilevel // Superkelas dengan tiga bidangnya dideklarasikan private class Kotak { private double lebar; private double tinggi; private double dalam; /* Perhatikan konstruktor ini. Ia mengambil sebuah objek bertipe data Kotak.*/ Kotak(Kotak ob) { // melewatkan objek kepada konstruktor lebar = ob.lebar; tinggi = ob.tinggi; dalam = ob.dalam; } // konstruktor dipakai ketika tiga-dimensi diberikan Kotak(double w, double h, double d) { lebar = w; tinggi = h; dalam = d; } // konstruktor dipakai ketika tidak ada dimensi diberikan Kotak() { lebar = -1; // menggunakan -1 untuk mengindikasikan tinggi = -1; // sebuah kotak dalam = -1; // yang tidak diinisialisasi } // konstruktor digunakan ketika kubik diciptakan Kotak(double pjg) { lebar = tinggi = dalam = pjg; } // menghitung dan menghasilkan volume double volume() { return lebar * tinggi * dalam; } } // BerakKotak sekarang mengimplementasikan semua konstruktor. class BeratKotak extends Kotak { double berat; // berat kotak // mengkonstruksi klon dari sebuah objek BeratKotak(BeratKotak ob) { // melewatkan objek kepada konstruktor super(ob); //memanggil konstruktor klon superkelas berat = ob.berat; } // konstruktor ketika semua parameter ditetapkan BeratKotak(double w, double h, double d, double m) { super(w, h, d); // memanggil konstruktor tiga-dimensi superkelas berat = m; } // konstruktor default BeratKotak() { super(); //memanggil konstruktor default dari superkelas berat = -1; } // konstruktor digunakan ketika kasus kubik BeratKotak(double pjg, double m) { super(pjg); //memanggil konstruktor kasus kubik dari superkelas berat = m; } } //Menambahkan biaya pengiriman. class Pengiriman extends BeratKotak { double biaya; //menciptakan klon objek Pengiriman(Pengiriman ob) { // melewatkan objek kepada konstruktor super(ob); biaya = ob.biaya; } //konstruktor ketika semua parameter ditetapkan Pengiriman(double w, double h, double d, double m, double c) { super(w, h, d, m); // memanggil konstruktor BeratKotak biaya = c; } //konstruktor default Pengiriman() { super(); // memanggil konstruktor default BeratKotak biaya = -1; } //konstruktor dipakai ketika kubik diciptakan Pengiriman(double len, double m, double c) { super(len, m); //memanggil konstruktor kubik BeratKotak biaya = c; } } public class DemoPengiriman { public static void main(String args[]) { Pengiriman pengiriman1 = new Pengiriman(10, 20, 15, 10, 3500); Pengiriman pengiriman2 = new Pengiriman(2, 3, 4, 0.76, 7500); double vol; vol = pengiriman1.volume(); System.out.println("Volume pengiriman1 = " + vol); System.out.println("Berat pengiriman1 = " + pengiriman1.berat); System.out.println("Biaya pengiriman1 Rp. " + pengiriman1.biaya); System.out.println(); vol = pengiriman2.volume(); System.out.println("Volume pengiriman2 = " + vol); System.out.println("Berat pengiriman2 = " + pengiriman2.berat); System.out.println("Biaya pengiriman2 Rp. " + pengiriman2.biaya); } }
Program ini menghasilkan keluaran berikut:
Volume pengiriman1 = 3000.0 Berat pengiriman1 = 10.0 Biaya pengiriman1 Rp. 3500.0 Volume pengiriman2 = 24.0 Berat pengiriman2 = 0.76 Biaya pengiriman2 Rp. 7500.0
Karena pewarisan, Pengiriman dapat menggunakan kelas Kotak dan BeratKotak, dan menambahkan informasi ekstra yang berkaitan dengan biaya pengiriman.
Contoh ini mengilustrasikan satu hal penting lain: super() selalu merujuk ke konstruktor superkelas terdekat. Jadi, super() pada BeratKotak memanggil konstruktor pada Kotak dan super() pada Pengiriman selalu memanggil konstruktor pada BeratKotak.
Kapan Konstruktor Dieksekusi
Ketika sebuah hierarki pewarisan diciptakan, bagaimanakah urutan eksekusi konstruktor untuk kelas-kelas yang membangun hierarki tersebut? Misalnya, jika diberikan subkelas B dan superkelas A, apakah konstruktor pada kelas A dieksekusi sebelum konstruktor kelas B, atau sebaliknya? Jawabannya adalah bahwa pada hierarki pewarisan, konstruktor pada superkelas dieksekusi lebih dahulu baru kemudian konstruktor pada subkelas dieksekusi. Program berikut mengilustrasikan kapan konstruktor-konstruktor dieksekusi:
// Mendemonstrasikan kapan konstruktor dieksekusi. // Menciptakan superkelas. class A { A() { System.out.println("Di dalam konstruktor A."); } } //Menciptakan subkelas yang mewarisi kelas A. class B extends A { B() { System.out.println("Di dalam konstruktor B."); } } //Menciptakan subkelas dengan mewarisi kelas B B. class C extends B { C() { System.out.println("Di dalam konstruktor C."); } } public class MemanggilKonstruktor { public static void main(String args[]) { C c = new C(); } }
Jika dijalankan, program ini menghasilkan keluaran:
Di dalam konstruktor A. Di dalam konstruktor B. Di dalam konstruktor C.
Seperti yang Anda lihat, konstruktor dieksekusi sesuai dengan rantai pewarisan, mulai dari konstruktor pada superkelas sampai konstruktor subkelas terbawah.
Jika super() dilibatkan dalam mekanisme pewarisan? Bagaimanakah urutan eksekusi konstruktor defaultnya? Program berikut membuktikan bahwa meskipun super() dilibatkan dalam pewarisan kelas, urutan eksekusi konstruktor tetap mulai dari konstruktor superkelas sampai konstruktor subkelas terbawah:
/* Program ini mendemonstrasikan urutan eksekusi konstruktor default * bila melibatkan super() */ // Superkelas dengan tiga bidangnya dideklarasikan private class Kotak { private double lebar; private double tinggi; private double dalam; // konstruktor dipakai ketika tidak ada dimensi diberikan Kotak() { lebar = -1; // menggunakan -1 untuk mengindikasikan tinggi = -1; // sebuah kotak dalam = -1; // yang tidak diinisialisasi System.out.println("Di dalam konstruktor Kotak."); } } // BerakKotak sekarang mengimplementasikan semua konstruktor. class BeratKotak extends Kotak { double berat; // berat kotak // konstruktor default BeratKotak() { super(); //memanggil konstruktor default dari superkelas berat = -1; System.out.println("Di dalam konstruktor BeratKotak."); } } //Menambahkan biaya pengiriman. class Pengiriman extends BeratKotak { double biaya; //konstruktor default Pengiriman() { super(); // memanggil konstruktor default BeratKotak biaya = -1; System.out.println("Di dalam konstruktor Pengiriman."); } } public class UrutanEksekusiKonstruktor { public static void main(String args[]) { Pengiriman pengiriman1 = new Pengiriman(); } }
Jika dijalankan, program ini menghasilkan keluaran:
Di dalam konstruktor Kotak. Di dalam konstruktor BeratKotak. Di dalam konstruktor Pengiriman.
Hal yang sama berlaku untuk konstruktor terparameterisasi atau konstruktor berparameter. Meskipun super() dilibatkan dalam mekanisme pewarisan, urutan eksekusi konstruktor tetap mulai dari konstruktor superkelas sampai konstruktor subkelas terbawah. Hal ini diilustrasikan pada program berikut ini:
// Program ini mendemonstrasikan uruan eksekusi konstruktor berparameter // Superkelas dengan tiga bidangnya dideklarasikan private class Kotak { private double lebar; private double tinggi; private double dalam; // konstruktor dipakai ketika tiga-dimensi diberikan Kotak(double w, double h, double d) { lebar = w; tinggi = h; dalam = d; System.out.println("Di dalam konstruktor Kotak."); } } // BerakKotak sekarang mengimplementasikan semua konstruktor. class BeratKotak extends Kotak { double berat; // berat kotak // konstruktor ketika semua parameter ditetapkan BeratKotak(double w, double h, double d, double m) { super(w, h, d); // memanggil konstruktor tiga-dimensi superkelas berat = m; System.out.println("Di dalam konstruktor BeratKotak."); } } //Menambahkan biaya pengiriman. class Pengiriman extends BeratKotak { double biaya; //konstruktor ketika semua parameter ditetapkan Pengiriman(double w, double h, double d, double m, double c) { super(w, h, d, m); // memanggil konstruktor BeratKotak biaya = c; System.out.println("Di dalam konstruktor Pengiriman."); } } public class UrutanEksekusiKonstruktorParameter { public static void main(String args[]) { Pengiriman pengiriman1 = new Pengiriman(10, 20, 15, 10, 3500); } }
Jika dijalankan, program ini menghasilkan keluaran:
Di dalam konstruktor Kotak. Di dalam konstruktor BeratKotak. Di dalam konstruktor Pengiriman.
Mendefinisikan-Ulang (Overriding) Metode
Para hierarki pewarisan kelas, ketika sebuah metode pada subkelas memiliki nama sama dan sidik tipe data (tipe data parameter dan jumlah parameter) sama seperti pada superkelasnya, maka metode pada subkelas tersebut dikatakan mendefinisikan ulang (override) metode pada superkelas. Ketika metode pada superkelas tersebut dipanggil di dalam subkelasnya, maka yang dipanggil adalah metode yang didefinisikan ulang oleh subkelas. Perhatikan program berikut:
// Mendefinisikan ulang (overriding) metode. class A { int i, j; A(int a, int b) { i = a; j = b; } // menampilkan i dan j void tampil() { System.out.println("i dan j: " + i + " " + j); } } class B extends A { int k; B(int a, int b, int c) { super(a, b); k = c; } // menampilkan k – ini mendefinisikan ulang tampil() pada A void tampil() { System.out.println("k: " + k); } } public class DefinisiUlangMetode { public static void main(String args[]) { B subOb = new B(1, 2, 3); subOb.tampil(); // ini memanggil tampil() pada B } }
Ketika dijalankan, program di atas menghasilkan keluaran:
k: 3
Ketika tampil() dipanggil pada sebuah objek bertipe B, maka versi tampil() yang didefinisikan di dalam B yang digunakan. Jadi, versi dari tampil() di dalam B mendefinisikan ulang (membatalkan) versi tampil() yang didefinisikan di dalam A.
Jika Anda ingin mengakses versi superkelas dari metode yang dibatalkan, Anda dapat melakukannya menggunakan super. Misalnya, pada versi dari kelas B ini, versi superkelas dari tampil() dipanggil di dalam versi subkelas. Perhatikan program berikut:
// Mendefinisikan ulang (overriding) metode. class A { int i, j; A(int a, int b) { i = a; j = b; } // menampilkan i dan j void tampil() { System.out.println("i dan j: " + i + " " + j); } } class B extends A { int k; B(int a, int b, int c) { super(a, b); k = c; } // menampilkan k – ini mendefinisikan ulang tampil() pada A void tampil() { super.tampil(); // ini memanggil tampil() dari superkelas A System.out.println("k: " + k); } } public class DefinisiUlangMetode2 { public static void main(String args[]) { B subOb = new B(1, 2, 3); subOb.tampil(); } }
Ketika dijalankan, program di atas menghasilkan keluaran:
i dan j: 1 2 k: 3
Pendefinisian-ulang (pembatalan) metode hanya terjadi ketika nama dan sidik tipe data (tipe data parameter dan jumlah parameter) dari dua metode identik. Jika tidak, maka yang terjadi hanyalah pembebanan metode. Misalnya, perhatikan versi modifikasi dari contoh sebelumnya:
// Mendefinisikan ulang (overriding) metode. class A { int i, j; A(int a, int b) { i = a; j = b; } // menampilkan i dan j void tampil() { System.out.println("i dan j: " + i + " " + j); } } class B extends A { int k; B(int a, int b, int c) { super(a, b); k = c; } // membebani tampil() void tampil(String pesan) { System.out.println(pesan + k); } } public class DefinisiUlangMetode3 { public static void main(String args[]) { B subOb = new B(1, 2, 3); subOb.tampil("Ini adalah nilai k: "); // ini memanggil tampil() pada B subOb.tampil(); // ini memanggil tampil() pada A } }
Ketika dijalankan, program di atas menghasilkan keluaran:
Ini adalah nilai k: 3 i dan j: 1 2
Menerapkan Pendefinisian-Ulang Metode
Perhatikan contoh praktis berikut yang menggunakan pendefinisian-ulang metode. Program berikut menciptakan sebuah superkelas Bangun yang menyimpan dimensi-dimensi dari sebuah objek dua-dimensi. Kelas itu juga mendefinisikan sebuah metode luas() yang menghitung luas objek. Program menderivasi dua subkelas dari Bangun. Subkelas pertama adalah PersegiPanjang dan yang kedua adalah SegiTiga. Setiap subkelas ini mendefinisikan-ulang metode luas() sehingga menghasilkan luas persegipanjang dan luas segitiga.
// Menggunakan polimorfisme. class Bangun { double dim1; double dim2; Bangun(double a, double b) { dim1 = a; dim2 = b; } double luas() { System.out.println("Luas bangun belum didefinisikan."); return 0; } } class PersegiPanjang extends Bangun { PersegiPanjang(double a, double b) { super(a, b); } // mendefinisikan-ulang luas() untuk persegipanjang double luas() { System.out.println("Luas Persegipanjang."); return dim1 * dim2; } } class SegiTiga extends Bangun { SegiTiga(double a, double b) { super(a, b); } // mendefinisikan luas() untuk segitiga double luas() { System.out.println("Luas segitiga."); return dim1 * dim2 / 2; } } public class HitungLuas { public static void main(String args[]) { Bangun f = new Bangun(10, 10); PersegiPanjang r = new PersegiPanjang(9, 5); SegiTiga t = new SegiTiga(10, 8); Bangun refbangun; refbangun = r; System.out.println("Luas = " + refbangun.luas()); refbangun = t; System.out.println("Luas = " + refbangun.luas()); refbangun = f; System.out.println("Luas = " + refbangun.luas()); } }
Ketika dijalankan, program di atas menghasilkan keluaran:
Luas Persegipanjang. Luas = 45.0 Luas segitiga. Luas = 40.0 Luas bangun belum didefinisikan. Luas = 0.0
No comments:
Post a Comment