Rabu, 19 Disember 2012

BAB 8 – PEWARISAN DAN POLIMORFISMA



3.1            Pengenalan

Pewarisan (inheritance) dan polimorfisma (polymorphism) merupakan dua ciri penting dalam PBO. Dengan menggunakan ciri-ciri ini, tugas menakrif kelas baru atau menambahbaik (ugrade) kelas sedia ada menjadi lebih mudah dan cekap. Bab ini akan melihat bagaimana kedua-dua proses ini boleh diaplikasikan dalam membangunkan aturcara.


3.2            Pewarisan (inheritance)


Pewarisan merujuk kepada proses yang membenarkan sesebuah objek mewarisi sifat dan kelakuan kelas sedia ada.

Dengan pewarisan, sekiranya kita ingin menakrifkan suatu kelas baru, yang mana sebahagian dari sifat dan kelakuannya sudah pun ditakrifkan dalam kelas lain yang sedia ada, maka kita boleh tetapkan supaya kelas baru tersebut ‘mewarisi’ sifat dan kelakuan kelas sedia ada tersebut. Dengan itu, semua sifat (pembolehubah) dan kelakuan yang ditakrifkan dalam kelas sedia ada tersebut akan wujud secara automatik di dalam kelas baru, tanpa perlu ditakrifkan semula di dalam kelas baru tersebut. Jadi, tak perlulah kita bersusah payah mencipta kelas baru tersebut dari awal!

Sebagi contoh, katakan kita telah sedia ada kelas Bulatan yang telah ditakrifkan mempunyai sifat: jejari, serta mempunyai kelakuan luas(). Katakan sekarang kita ingin bangunkan pula kelas Sfera dengan sifat: jejari, dan kelakuan luas() dan isipadu(). Jika diamati, sifat jejari dan kelakuan luas() dari kelas Sfera lebih kurang serupa fungsinya dengan pembolehubah jejari dan kelakuan luas() yang telah ditakrifkan dalam kelas Bulatan. Dengan itu, sekarang ini kita ada dua pilihan: menakrifkan semula kelas Sfera (seperti Rajah 3-1(a) ) atau kita boleh takrifkan kelas Sfera sebagai waris kepada kelas Bulatan (rujuk Rajah 3-1(b) ). Jika kita pilih pilihan pertama, kita perlu bina kelas Sfera dari awal!. Sebaliknya, jika kita pilih pilihan kedua, kita tidak perlu meakrifkan kelas Sfera dari awal. Sebaliknya, kita hanya perlu menambah dan mengubahsuai sahaja kelas Bulatan. Jadi lebih baik kita pilih pilihan kedua iaitu dengan menakrifkan kelas Sfera sebagai waris kepada kelas Bulatan . Dengan ini, kelas Sfera tidak perlu lagi ditakrifkan dengan pembolehubah jejari dan kelakuan luas() kerana ia akan wujud secara automatik, hasil dari pewarisan itu. jadi, kita hanya perlu tambahkan satu kelakuan baru dalam kelas Sfera iaitu isipadu().

3.2.1        Superkelas dan subkelas

Suatu kelas yang mewariskan sifat dan kelakuannya kepada suatu kelas lain dipanggil superkelas (superclass) atau kelas ibubapa (parent), dan kelas yang mewarisi sifat dan kelakuan dari kelas yang lain dipanggil subkelas (subclassatau kelas anak (child).


Hubungan pewarisan merupakan suatu hubungan berhierarki di antara kelas. Sebagai contoh, pada Rajah 3-3, kelas Geometri mempunyai 2 subkelas iaitu Bulatan dan Segiempat. Jadi, kita katakan Geometri adalah superkelas kepada kelas Bulatan dan Segiempat. Sementara itu, kelas Sfera pula adalah subkelas kepada kelas Bulatan. Maka, kita katakan pula Bulatan adalah superkelas bagi kelas Sfera.


Hubungan antara subkelas dan superkelas juga boleh dilihat sebagai hubungan ‘adalah suatu’ (‘is-a’ relationship). Misalnya, kita katakan Bulatan adalah suatu Geometri (Bulatan is-a Geometri). Begitu juga, Sfera adalah suatu Bulatan (Sfera is-a Bulatan).


3.2.2        Konsep pewarisan tunggal

Java hanya membenarkan pewarisan tunggal (single inheritance) sahaja, bermakna satu kelas hanya boleh mewarisi sifat dan kelakuan dari satu kelas sahaja. Ini bagi mengelakkan kekeliruan yang timbul akibat pewarisan berbilang (multiple inheritance). Sesetangah bahasa pengaturcaraan seperti C++, membenarkan pewarisan berbilang iaitu satu kelas boleh mewarisi sifat dan kelakuan dari lebih dari satu kelas. Rajah 3-4(a) dan Rajah 3-4(b) menjelaskan perbezaan antara pewarisan tunggal dan pewarisan berbilang. Dalam Rajah 3-4(a) kelas Sfera hanya mempunyai satu superkelas sahaja iaitu Bulatan. Begitu juga kelas Bulatan ada hanya satu superkelas sahaja iaitu Geometri. Jadi kedua-dua hubungan pewarisan ini adalah hubungan pewarisan tunggal. Sementara itu, dalam Rajah 3-4(b), kelas Selinder mempunyai 2 superkelas, iaitu Segiempat dan Bulatan. Jadi kelas Selinder mengamalkan pewarisan berbilang.


3.3            Polimorfisma (Polymorphism)

Secara umum, polimorfisma adalah proses menakrifkan dua atau lebih kelakuan yang sama namanya tetapi melakukan tugas-tugas yang berbeza. Dalam Java, polimorfisma boleh diaplikasikan dalam dua keadaan: oeverloading dan overriding.


3.3.1        Overloading

Overloading merujuk kepada proses menakrif satu atau lebih kelakuan (atau konstruktur) dengan nama yang sama dalam satu kelas yang sama, tetapi setiap satu kelakuan tersebut akan bertindak dengan cara yang berbeza.

Sebagai contoh, kelas Bulatan boleh ditakrifkan seperti berikut:

class Bulatan
{
     public double jejari;

     // kelakuan luas() pertama
public double luas()
     {
           return (Math.PI * jejari * jejari);
     }

     // kelakuan luas() kedua
     public double luas(double rad)
     {
           jejari = rad;
           return (Math.PI * jejari * jejari);
     }
}

Dalam kelas Bulatan ini, terdapat dua kelakuan luas() yang setiap satunya akan melaksanakan tugas yang berbeza. Maknanya, kita telah di-‘overload’-kan kelakuan luas()

Kita boleh gunakan kelas Bulatan ini seperti berikut:

Bulatan b1 = new Bulatan(); 
Bulatan b2 = new Bulatan(); 

b1.jejari = 2.5;
System.out.println(“Luas: “ + b1.luas());   // guna luas() pertama

System.out.println(“Luas: “ + b2.luas(3.0)); // guna luas() kedua

Walaupun kelakuan yang dioverload boleh mempunyai nama yang sama, tetapi kedua-dua kelakuan tersebut mestilah mempunyai tandatangan (signature) yang berbeza. Tandatangan kelakuan ditentukan oleh jenis, bilangan dan susunan parameternya.

Contohnya, tiga kelakuan berikut mempunyai tandatangan yang berlainan:

public void myMethodA(int a)
{
     :
}

public void myMethodA(int a, double b)
{
     :
}

public void myMethodA(double a, int b)
{
     :
}


Sementara, dua kelakuan berikut, sebenarnya mempunyai tandatangan yang sama:

public void myMethodB(int a)
{
     :
}

public int myMethodB(int x)  // parameter sama jenis
{
     :
}

Begitu juga dengan dua kelakuan berikut, masing-masing mempunyai tandatangan yang sama

public double myMethodC(double a, int b)
{
     :
}

public double myMethodC(double b, int a)  // parameter sama susunan
{
     :
}


Oleh itu, kita perlu pastikan agar kelakuan yang ingin dioverload perlu mempunyai tandatangan yang berbeza. Jika tidak, kita akan mendapat ralat semasa proses mengompil.


3.3.2        Overriding

Proses overriding berlaku apabila suatu kelas yang mewarisi suatu kelakuan dari kelas lain mengubahsuai kelakuan yang diwarisinya itu.

Kadangkala kelakuan yang diwarisi perlu diperbaiki, supaya ia sesuai digunakan di dalam kelas yang mewarisinya. Sebagai contoh, jika kelas Sfera mewarisi kelas Bulatan, maka kelas Sfera akan mewarisi kelakuan luas() dari kelas Bulatan tersebut. Bagaimanapun, kelakuan luas() yang ditakrifkan dalam kelas Bulatan menggunakan formula pengiraan yang tidak sama dengan kelakuan luas() bagi kelas Sfera. Jadi, kelas Sfera perlu override kelakuan luas() yang diwarisinya itu supaya ia menggunakan formula yang lebih sesuai dengan kelas Sfera.

Tidak seperti proses overloading, kelakuan yang akan override kelakuan yang diwarisi mestilah mempunyai tandatangan yang serupa dengan kelakuan yang ingin dioveride tersebut. Jika tidak, kelakuan yang diwarisi itu tidak akan di override.


3.3.3        Perbezaan antara overloading dan overriding

Perbezaan antara overloading dan overriding ditunjukkan secara ringkas di dalam Jadual 3-1

Jadual 3-1: Perbezaan antara overloading dan overriding
Konsep
Definisi
Keterangan lengkap
Overloading
Lebih daripada satu kelakuan dengan nama yang sama dalam kelas  yang sama
Tandatangan bagi setiap kelakuan adalah berbeza

Overriding
Mengubahsuai kelakuan yang diwarisi dari  superkelas dengan perlaksanaan yang sesuai dengan subkelas tersebut
Kedua-dua kelakuan mesti mempunyai tandatangan yang sama



3.4            Menakrifkan kelas dengan pewarisan dan polimorfisma


Katakan kita ingin bangunkan aturcara untuk digunakan di sebuah kedai video. Jadi, untuk melaksanakan tugas ini, salah satu kelas penting yang perlu ditakrifkan dahulu adalah kelas VideoTape.

Setelah mengkaji, kita telah membuat keputusan untuk menakrifkan kelas VideoTape dengan spesifikasi seperti berikut:

Kelas: VideoTape.java

Sifat
Kegunaan / fungsi
Jenis
- title
Menyimpan tajuk video
String
- length
Meyimpan jumlah masa tayangan video(dalam minit).
int
- avail
Menentukan sama ada video tersebut telah disewa (tiada di dalam kedai) atau belum disewa (ada didalam kedai).
boolean

Kelakuan
Fungsi
Jenis
+ Video(ttl, lgth)
Konstruktor yang akan menetapkan nilai bagi title (melalui ttl) dan length (lgth) bagi satu objek VideoTape yang baru. Nilai avail pula akan ditetapkan terus (tanpa ambil nilai dari parameter) sebagai true.
-
+ show()
Kelakuan yang akan memaparkan nilai-nilai titlelength dan avail bagi sesuatu objek VideoTape.
void

Dari spesifikasi ini, kita bangunkan kelas VideoTape seperti berikut:

class VideoTape
{
     protected String title;
     protected int length;
     protected boolean avail;

  
     public VideoTape(String ttl, int lngth)
     {
           title = ttl;
           length = lngth;
           avail = true;
     }

     public void show()
     {
           System.out.println(“\nTajuk        : " + title);
           System.out.println(“Jangkamasa   : " + length + “ min.");
           System.out.println(“Belum disewa : "  + avail);
     }
}

Bagi menguji kelas VideoTape ini, kita bangunkan kelas berikut:


class TapeStore {
     public static void main ( String args[] ) {
           VideoTape item1 = new VideoTape("Jaws", 120);
           VideoTape item2 = new VideoTape("Star Wars", 160);
           item1.show();
           item2.show();
     }
}

Hasil output bagi kelas TapeStore adalah seperti berikut:

Tajuk        : Jaws
Jangkamasa   : 120 min.
Belum disewa : true

Tajuk        : Star Wars
Jangkamasa   : 160 min.
Belum disewa : true

Selepas beberapa hari, kita dapati kebanyakan pita video mempunyai masa tayangan yang sama, iaitu 120 minit. Jadi, untuk memudahkan tugas meincipta objek VideoTape yang baru, kita putuskan untuk overloadkan konstruktor supaya konstruktor baru ini akan menetapkan masa tayangan kepada 120 secara automatik. Jadi, kelas VideoTape yang telah dikemaskini akan jadi seperti berikut:

public class VideoTape
{
     String title;
     int length;
     boolean avail;

  
     public VideoTape(String ttl, int lngth)
     {
           title = ttl;
           length = lngth;
           avail = true;
     }

     public VideoTape(String ttl)
     {
           title = ttl;
           length = 120;
           avail = true;
     }

     public void show()
     {
           System.out.println(“\nTajuk        : " + title);
           System.out.println(“Jangkamasa   : " + length + “ min.");
           System.out.println(“Belum disewa : "  + avail);
     }
}

Jadi
, menggunakan kelas VideoTape yang baru ini, kita boleh bina objek VideoTape dengan 2 cara yang berbeza seperti berikut:

class TapeStore  {
     public static void main ( String args[] )       {
           VideoTape item1 = new VideoTape("Jaws");
           VideoTape item2 = new VideoTape("Star Wars", 160);
           item1.show();
           item2.show();
     }
}

Hasil output bagi kelas TapeStore sekarang adalah seperti berikut:

Tajuk        : Jaws
Jangkamasa   : 120 min.
Belum disewa : true

Tajuk        : Star Wars
Jangkamasa   : 160 min.
Belum disewa : true

Katakan pula selepas beberapa hari, kita dapati adalah penting untuk memasukkan nama pengarah dan rating (U, 18SG, 18SX, 18PA atau 18PL) bagi pita-pita video cereka (movie). Bagaimanapun, di dalam kedai video, terdapat juga pita-pita dokumentari, konsert dan sebagainya yang tak perlukan nama pengarah dan rating. Jadi kita tidak boleh dengan mudah mengubahsuai kelas VideoTape. Cara yang paling berkesan ialah dengan mendefinisi kelas baru khas untuk pita-pita video cereka. Kita namakan kelas ini sebagai MovieTape.

Kelas MovieTape ada persamaan dengan kelas VideoTape, tetapi dengan sedikit penambahan dan pengubahsuaian. Jadi kita putuskan untuk menakrif kelas MovieTape sebagai mewarisi kelas VideoTape. Jadi kelas MovieTape akan ditakrifkan seperti berikut:

class MovieTape extends VideoTape
{
     protected String director;
     protected String rating;
    
     public MovieTape(String ttl, int lngth, String dir, String rt)
     {
           super(ttl, lngth);
           director = dir;
           rating = rt;
     }

     public MovieTape(String ttl, String dir, String rt)
     {
           super(ttl);
           director = dir;
           rating = rt;
     }

     // override kelakuan show() dari kelas VideoTape
     public void show()
     {
           super.show();
           System.out.println(“Pengarah     : "  + director);
           System.out.println(“Spesifikasi  : "  + rating);
     }
}

Dalam kelas MovieTape ini, terdapat 2 perkara baru: katakunci extends dan super
3.4.1        Katakunci extends

Kelas MovieTape menggunakan katakunci extends pada pengepala kelasnya. Katakunci extends ini adalah katakunci yang perlu diletakkan bagi menyatakan bahawa MovieTape adalah mewarisi kelas VideoTape.


3.4.2        Konstruktor bagi subkelas dan kelakuan super()

Oleh kerana MovieTape adalah subkelas kepada VideoTape, maka semua pembolehubah dan kelakuan VideoTape akan wujud secara automatik di dalam kelas MovieTape. Bagaimanapun, terdapat pengecualian dalam peraturan ini bagi konstruktor. Konstruktor bagi superkelas tidak akan diwarisi oleh subkelas. Oleh itu konstruktor bagi kelas MovieTape perlu ditakrifkan semula. 

Untuk mencapai konstruktor bagi superkelas, kita boleh gunakan kelakuan khas dipanggil super(). Kelakuan super() membolehkan konstruktor bagi superkelas dicapai oleh konstruktor bagi subkelas. Jadi pernyataan

super(ttl, lngth);

dalam konstruktor pertama bagi kelas MovieTape akan memanggil dan melaksanakan konstruktor pertama bagi kelas VideoTape. Begitu juga, pernyataan

super(ttl);

dalam konstruktor kedua bagi kelas MovieTape akan memanggil dan melaksanakan konstruktor kedua bagi kelas VideoTape.

Semasa menggunakan kelakuan super(), fakta-fakta berikut perlu diambil perhatian:
·                Ia membolehkan capaian kepada konstruktor superkelas
·                Ia hanya boleh dicapai di dalam konstruktor bagi subkelas.
·                Menggunakan super() dalam konstruktor subkelas adalah suatu pilihan sahaja, bukan kemestian
·                Jika menggunakan super(), ia mesti diletakkan pada permulaan baris dalam badan konstruktor subkelas.
·                Jika super() tidak digunakan dalam konstruktor subkelas, Java tetap akan melaksanakan konstruktor default bagi superkelas setiap kali objek baru subkelas dicipta.


3.4.3        Override kelakuan dan penggunaan katakunci super

Selain konstruktor, kita juga perlu override kelakuan show() kerana kelakuan show() yang diwarisi hanya akan memaparkan titlelength dan avail sahaja, sedangkan kelas MovieTape ada pembolehubah director dan rating yang juga perlu dipaparkan. Walaupun begitu, kelakuan show() yang ditakrifkan dalam kelas VideoTape masih lagi boleh digunakan. Untuk laksanakan kelakuan show() dalam kelas VideoTape (superkelas) di dalam kelas MovieTape, kita perlu gunakan katakunci super. Katakunci super adalah ganti nama bagi membolehkan kita merujuk superkelas bagi sesuatu subkelas. Jadi, pernyataan

super.show();

dalam kelakuan show() bagi kelas MovieTape akan memanggil kelakuan show() dalam superkelas bagi MovieTape iaitu kelas VideoTape.

Catatan:
Kita perlu faham perbezaan antara kelakuan super() dan katakunci super:
·                super() – merujuk kepada konstruktor pada superkelas
·                super.namaKelakuan() – merujuk kepada kelakuan yang berada dalam superkelas
·                super.namaPembolehubah – merujuk kepada pembolehubah yang berada dalam superkelas

Tiada ulasan:

Catat Ulasan

Related Posts Plugin for WordPress, Blogger...