Saturday, March 17, 2018

7. Pewarisan



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.

// 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: