Saturday, December 24, 2016

Bab 10. Pemrograman C# Belajar Dari Contoh



10. Pewarisan







Pewarisan adalah salah satu dari tiga prinsip utama pemrograman berorientasi objek, karena dengan menggunakannya penciptaan klasifikasi hirarkis dapat dilakukan. Hal ini berarti bahwa Anda dapat menciptakan sebuah kelas umum yang mendefinisikan watak umum dari sehimpunan item yang berelasi. Kelas tersebut kemudian diwarisi oleh satu atau lebih kelas lain yang lebih spesifik, yang masing-masing menambahkan beberapa item yang unik pada kelasnya.

Dalam bahasa C#, sebuah kelas yang diwarisi dinamakan dengan kelas basis. Kelas yang mewarisi disebut dengan kelas terderivasi. Oleh karena itu, kelas terderivasi merupakan versi terspesialisasi dari kelas basis. Kelas terderivasi mewarisi semua variabel, metode, properti, dan indekser yang didefinisikan oleh kelas basis dan menambahkan elemen-elemen uniknya sendiri.


Dasar Pewarisan
C# mendukung pewarisan dengan mengijinkan satu kelas bergabung dengan kelas lain. Ini dilakukan dengan menspesifikasi kelas basis ketika kelas terderivasi dideklarasikan. Pembahasan pewarisan akan dimulai dengan penyajian sebuah contoh. Kelas berikut, dinamakan BangunDuaD, menyimpan lebar dan tinggi suatu objek dua dimensi, seperti persegi, persegi-panjang, segitiga, dan lainnya.

// Sebuah kelas untuk objek-objek dua dimensi.
class BangunDuaD {
  public double Lebar;
  public double Tinggi;
 
  public void TampilDim() {
  Console.WriteLine("Lebar dan tinggi adalah " +
                    Lebar + " dan " + Tinggi);
  }
}

BangunDuaD dapat dipakai sebagai kelas basis (jadi, sebagai titik awal) untuk kelas-kelas yang mendeskripsikan tipe spesifik dari objek dua dimensi. Sebagai contoh, program berikut menggunakan BangunDuaD untuk menderivasi sebuah kelas yang dinamakan Segitiga. Perhatikan bagaimana cara Segitiga dideklarasikan:

// Sebuah hirarki kelas sederhana.
using System;

// Sebuah kelas untuk objek dua dimensi.
class BangunDuaD {
  public double Lebar;
  public double Tinggi;

  public void TampilDim() {
    Console.WriteLine("Lebar dan tinggi adalah " +
                      Lebar + " dan " + Tinggi);
  }
}

// Segitiga diderivasi dari BangunDuaD.
class Segitiga : BangunDuaD {
  public string Jenis; // jenis segitiga

  // Menghasilkan luas segitiga.
  public double Luas() {
    return Lebar * Tinggi / 2;
  }

  // Menampilkan jenis segitiga.
  public void TampilJenis() {
    Console.WriteLine("Segitiga berjenis " + Jenis);
  }
}

class Bangun {
  static void Main() {
    Segitiga t1 = new Segitiga();
    Segitiga t2 = new Segitiga();

    t1.Lebar = 4.0;
    t1.Tinggi = 4.0;
    t1.Jenis = "sama-sisi";

    t2.Lebar = 8.0;
    t2.Tinggi = 12.0;
    t2.Jenis = "tak sama-sisi";

    Console.WriteLine("Info untuk t1: ");
    t1.TampilJenis();
    t1.TampilDim();

    Console.WriteLine("Luas adalah " + t1.Luas());
    Console.WriteLine();

    Console.WriteLine("Info untuk t2: ");
    t2.TampilJenis();
    t2.TampilDim();

    Console.WriteLine("Luas adalah " + t2.Luas());
  }
}

Keluaran program ditampilkan di sini:

Info untuk t1:
Segitiga berjenis sama-sisi
Lebar dan tinggi adalah 4 dan 4
Luas adalah 8

Info untuk t2:
Segitiga berjenis tak sama-sisi
Lebar dan tinggi adalah 8 dan 12
Luas adalah 48

Kelas Segitiga menciptakan sebuah tipe spesifik dari BangunDuaD, pada kasus ini, sebuah segitiga. Kelas Segitiga mewarisi semua milik BangunDuaD dan menambahkan bidang Jenis, metode Luas(), dan metode TampilJenis(). Deskripsi mengenai jenis segitiga disimpan di dalam Jenis; Luas() menghitung dan menjadikan luas segitiga sebagai nilai balik; dan TampilJenis() menampilkan jenis segitiga.

Perhatikan sintaks yang digunakan Segitiga untuk mewarisi BangunDuaD:

class Segitiga : BangunDuaD {

Sintaks ini dapat digeneralisir. Kapanpun sebuah kelas mewarisi kelas lain, nama kelas basis ditempatkan setelah nama kelas terderivasi, dipisahkan dengan titik dua. Dalam C#, sintaks untuk mewarisi sebuah kelas cukup sederhana dan mudah digunakan.

Karena Segitiga mewarisi semua anggota dari kelas basisnya, BangunDuaD, ia dapat mengakses Tinggi dan Lebar di dalam Luas(). Juga, di dalam Main(), objek t1 dan t2 dapat merujuk ke Lebar dan Tinggi secara langsung. Gambar 10.1 menggambarkan secara konseptual bagaimana BangunDuaD menjadi bagian dari Segitiga.

Meskipun BangunDuaD adalah kelas basis bagi Segitiga, ia juga secara utuh merupakan kelas independen dan berdiri sendiri. Menjadi kelas basis bagi sebuah kelas terderivasi tidak berarti bahwa kelas basis tidak bisa digunakan secara mandiri. Sebagai contoh, berikut adalah kode yang valid:

BangunDuaD bangun = new BangunDuaD();

bangun.Lebar = 10;
bangun.Tinggi = 20;

bangun.TampilDim();

Tentu saja, sebuah objek BangunDuaD tidak mengenal atau tidak dapat mengakses sembarang kelas yang diderivasi dari BangunDuaD.

Gambar 10.1 Penggambaran konseptual atas kelas Segitiga

Bentuk umum dari sebuah deklarasi class yang mewarisi kelas basis ditunjukkan di sini:

class nama-kelas-terderivasi : nama-kelas-basis {
  // tubuh kelas
}

Anda hanya dapat menspesifikasi satu kelas basis bagi kelas terderivasi yang Anda ciptakan. C# tidak mendukung pewarisan kelas basis jamak bagi satu kelas terderivasi. (Ini berbeda dari C++, dimana Anda dapat mewarisi beberapa kelas basis bagi satu kelas terderivasi. Hati-hati akan hal ini ketika mengkonversi kode C++ menjadi C#). Anda dapat menciptakan sebuah hirarki pewarisan dimana di dalamnya sebuah kelas terderivasi bisa menjadi kelas basis bagi kelas terderivasi lain. Pada semua kasus, kelas terderivasi mewarisi semua anggota dari kelas basisnya, termasuk variabel, metode, properti, dan indekser.

Keuntungan utam dari pewarisan adalah bahwa begitu Anda menciptakan sebuah kelas basis yang mendefinisikan atribut umum bagi sekumpulan objek, ia dapat dipakai untuk menciptakan sejumlah kelas terderivasi yang lebih spesifik. Setiap kelas terderivasi bisa mendefinisikan klasifikasinya sendiri. Sebagai contoh, berikut adalah kelas terderivasi lain dari BangunDuaD yang mengenkapsulasi sebuah persegi-panjang:

// Sebuah kelas terderivasi dari BangunDuaD untuk persegi-panjang.
class PersegiPanjang : BangunDuaD {
  // Menghasilkan true jika persegipanjang sama sisi.
  public bool ApaKuadratis() {
    if(Lebar == Tinggil) return true;
    return false;
  }

  // Menghasilkan luas persegipanjang.
  public double Luas() {
    return Lebar * Tinggi;
  }
}

Kelas PersegiPanjang mewarisi BangunDuaD dan menambahkan metode ApaKuadratis(), yang menentukan jika persegipanjang adalah sama sisi, dan Luas(), yang menghitung luas persegipanjang.


Akses Anggota Dan Pewarisan
Seperti dijelaskan pada Bab 7, anggota kelas seringkali dideklarasikan private untuk mencegah penggunaan yang tidak diotorisasi. Pewarisan sebuah kelas tidak melanggar batasan akses privat. Jadi, meskipun kelas terderivasi mewarisi semua anggota dari kelas basisnya, ia tidak dapat mengakses anggota kelas basis yang dideklarasikan private. Sebagai contoh, jika, seperti ditampilkan di sini, Lebar dan Tinggi dideklarasikan private di dalam BangunDuaD, maka Segitiga tidak dapat mengakses kedua bidang tersebut:

// Akses terhadap anggota private tidak diwarisi.

// Contoh ini tidak akan bisa dikompilasi.
using System;

// Sebuah kelas untuk objek dua dimensi.
class BangunDuaD {
  double Lebar; // sekarang private
  double Tinggi; // sekarang private

  public void TampilDim()
  {
     Console.WriteLine("Lebar dan tinggi adalah " +
                       Lebar + " dan " + Tinggi);
  }
}

// Segitiga diderivasi dari BangunDuaD.
class Segitiga : BangunDuaD
{
    public string Jenis; // jenis segitiga
   
    // Menghasilkan luas segitiga.
    public double Luas()
    {
        return Lebar * Tinggi / 2; // Error, tidak bisa mengakses anggota private
    }
   
    // Menampilkan jenis segitiga.
    public void TampilJenis()
    {
        Console.WriteLine("Segitiga adalah " + Jenis);
    }
}

Kelas Segitiga tidak bisa dikompilasi karena penggunaan Lebar dan Tinggi di dalam metode Luas() adalah ilegal. Karena Lebar dan Tinggi sekarang dideklarasikan private, maka keduanya hanya bisa diakses oleh anggota-anggota dari kelasnya sendiri (pada kasus ini, BangunDuaD). Kelas terderivasi tidak dapat mengaksesnya.

Pertama-tama, Anda mungkin berpikir bahwa ini merupakan pembatasan serius karena kelas terderivasi tidak bisa mengakses anggota private dari kelas basis. Namun, hal ini tidak selalu benar; C# menyediakan berbagai solusi atas masalah ini. Salah satunya adalah menggunakan anggota protected, yang disajikan pada bagian selanjutnya. Solusi kedua adalah menggunakan properti publik untuk menyediakan akses terhadap data privat.

Seperti dijelaskan pada bab sebelumnya, sebuah properti dapat dipakai untuk mengelola akses terhadap variabel instans. Sebagai contoh, Anda dapat mengekang nilai dari variabel instans, atau Anda dapat menjadikannya menjadi variabel read-only. Dengan mendeklarasikan sebuah properti sebagai public, dan tetap mendeklarasikan variabel instansnya sebagai private, kelas terderivasi masih dapat menggunakan properti tersebut untuk mengakses variabel private secara tidak langsung.

Berikut adalah hasil penulisan-ulang kelas BangunDuaD yang menjadikan Lebar dan Tinggi sebagai properti. Dalam proses tersebut, program memastikan bahwa nilai dari Lebar dan Tinggi harus positif.

// Menggunakan properti publik untuk menetapkan dan mendapatkan anggota private.
using System;

// Sebuah kelas untuk objek dua dimensi.
class BangunDuaD {
  double lebar_bangun; // sekarang private
  double tinggi_bangun; // sekarang private

  // Properti untuk Lebar dan Tinggi.
  public double Lebar {
    get { return lebar_bangun; }
    set { tinggi_bangun = value < 0 ? -value : value; }
  }

  public double Tinggi {
    get { return tinggi_bangun; }
    set { tinggi_bangun = value < 0 ? -value : value; }
  }

  public void TampilDim() {
    Console.WriteLine("Lebar dan tinggi adalah " +
                      Lebar + " dan " + Tinggi);
  }
}

// Sebuah kelas terderivasi dari BangunDuaD untuk segitiga.
class Segitiga : BangunDuaD {
  public string Jenis; // jenis segitiga

  // Menghasilkan luas segitiga.
  public double Luas() {
    return Lebar * Tinggi / 2;
  }

  // Menampilkan jenis segitiga.
  public void TampilJenis() {
    Console.WriteLine("Segitiga adalah " + Jenis);
  }
}

class Bangun2 {
  static void Main() {
    Segitiga t1 = new Segitiga();
    Segitiga t2 = new Segitiga();

    t1.Lebar = 4.0;
    t1.Tinggi = 4.0;
    t1.Jenis = "sama-sisi";

    t2.Lebar = 8.0;
    t2.Tinggi = 12.0;
    t2.Jenis = "tak sama-sisi";

    Console.WriteLine("Info untuk t1: ");
    t1.TampilJenis();
    t1.TampilDim();

    Console.WriteLine("Luas adalah " + t1.Luas());
    Console.WriteLine();

    Console.WriteLine("Info untuk t2: ");
    t2.TampilJenis();
    t2.TampilDim();

    Console.WriteLine("Luas adalah " + t2.Luas());
  }
}

Pada versi ini, properti Lebar dan Tinggi menyediakan akses bagi anggota privat, lebar_bangun dan tinggi_bangun, yang secara aktual menyimpan nilai. Oleh karena itu, meskipun lebar_bangun dan tinggi_bangun dideklarasikan private di dalam BangunDuaD, nilainya masih dapat ditetapkan dan diperoleh melalui properti publik terkait.


Menggunakan Akses Terproteksi
Seperti dijelaskan sebelumnya, anggota privat suatu kelas basis tidak dapat diakses oleh kelas terderivasi. Ini mengimplikasikan bahwa jika Anda ingin kelas terderivasi untuk dapat memiliki akses terhadap anggota kelas basis, maka anggota tersebut harus dideklarasikan public. Tentu saja, membuat anggota menjadi publik beresiko membuatnya dapat diakses oleh semua kode di luar kelas. Implikasi ini bisa diperbaiki dengan menggunakan anggota terproteksi. Anggota terproteksi adalah anggota publik di dalam suatu hirarki pewarisan kelas, tetapi privat di luar hirarki pewarisan tersebut.

Anggota terproteksi diciptakan menggunakan pemodifikasi akses protected. Ketika anggota suatu kelas dideklarasikan sebagai protected, anggota tersebut, dengan satu pengecualian, adalah privat. Pengecualian terjadi ketika anggota terproteksi diwariskan. Pada kasus tersebut, anggota terproteksi dari kelas basis menjadi anggota terproteksi kelas terderivasi dan, oleh karena itur, dapati diakses oleh kelas terderivasi. Berikut adalah contoh sederhana yang menggunakan protected:

// Demonstrasi protected.
using System;

class B
{
    protected int i, j; // private pada B, tetapi bisa diakses D
   
    public void Tetapkan(int a, int b)
    {
        i = a;
        j = b;
    }
   
    public void Tampil()
    {
        Console.WriteLine(i + " " + j);
    }
}

class D : B
{
    int k; // private
   
    // D dapat mengakses i dan j dari kelas basis B
    public void Tetapkank()
    {
        k = i * j;
    }
   
    public void Tampilk()
    {
        Console.WriteLine(k);
    }
}

class DemoTerproteksi
{
    static void Main()
    {
        D ob = new D();

        ob.Tetapkan(2, 3); // OK, dikenali oleh D
        ob.Tampil(); // OK, dikenali oleh D

        ob.Tetapkank(); // OK, bagian dari D
        ob.Tampilk(); // OK, bagian dari D
    }
}

Pada contoh ini, karena B diwarisi oleh D dan karena i dan j dideklarasikan sebagai protected di dalam B, metode Tetapkank() dapat mengakses keduanya. Jika i dan j dideklarasikan sebagai private oleh B, maka D tidak akan bisa mengaksesnya, dan program tidak akan bisa dikompilasi.

Seperti public dan private, status protected tetap bertahan pada sebuah anggota tanpa memandang berapa banyak lapisan pewarisan yang terlibat. Oleh karena itu, ketika kelas terderivasi digunakan sebagai kelas basis bagi kelas terderivasi lain, anggota terproteksi dari kelas basis awal yang diwarisi kelas terderivasi pertama juga diwarisi sebagai anggota terproteksi oleh kelas terderivasi kedua.


Konstruktor Dan Pewarisan
Dalam sebuah hirarki pewarisan, dimungkinakn bagi kelas basis dan kelas terderivasi untuk mempunyai konstruktornya sendiri. Hal ini menimbulkan satu pertanyaan penting: Konstruktor mana yang bertanggung jawab untuk menciptakan objek kelas terderivasi? Konstruktor pada kelas basis, konstruktor pada kelas terderivasi, atau keduanya? Ini jawabannya: Konstruktor pada kelas basis menciptakan porsi kelas basis pada objek, dan konstruktor pada kelas terderivasi menciptakan porsi kelas terderivasi. Jadi, konstruksinya terpisah. Contoh sebelumnya mengandalkan pada konstruktor default yang diciptakan secara otomatis oleh C#. Namun, pada berbagai masalah riil, kebanyakan kelas mendefinisikan konstruktor. Di sini, Anda akan melihat bagaimana menangani situasi ini.

Ketika hanya kelas terderivasi yang mendefinisikan konstruktor, prosesnya akan sederhana: Hanya konstruktor kelas terderivasi yang bertanggung jawab menciptakan objek. Porsi kelas basis pada objek dikonstruksi secara otomatis menggunakan konstruktor default. Sebagai contoh, berikut adalah versi hasil penulisan-ulang dari Segitiga, yang mendefinisikan sebuah konstruktor. Program juga mendeklarasikan Jenis sebagai private karena sekarang ditetapkan oleh konstruktor.

// Menambahkan sebuah konstruktor pada Segitiga.
using System;

// Sebuah kelas untuk objek dua dimensi.
class BangunDuaD
{
    double lebar_bangun; // sekarang private
    double tinggi_bangun; // sekarang private

    // Properti untuk Lebar dan Tinggi.
    public double Lebar
    {
        get { return lebar_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public double Tinggi
    {
        get { return tinggi_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public void TampilDim()
    {
        Console.WriteLine("Lebar dan tinggi adalah " +
                          Lebar + " dan " + Tinggi);
    }
}

// Sebuah kelas terderivasi dari BangunDuaD untuk segitiga.
class Segitiga : BangunDuaD
{
    string Jenis; // jenis segitiga

    // Konstruktor.
    public Segitiga(string s, double w, double h) {
      Lebar = w; // inisialisasi kelas basis
      Tinggi = h; // inisialisasi kelas basis
      Jenis = s; // inisialisasi kelas basis
    }
   
    // Menghasilkan luas segitiga.
    public double Luas()
    {
        return Lebar * Tinggi / 2;
    }

    // Menampilkan jenis segitiga.
    public void TampilJenis()
    {
        Console.WriteLine("Segitiga adalah " + Jenis);
    }
}

class Bangun3
{
    static void Main()
    {
        Segitiga t1 = new Segitiga("sama-sisi", 4.0, 4.0);
        Segitiga t2 = new Segitiga("tak sama-sisi", 8.0, 12.0);

        Console.WriteLine("Info untuk t1: ");
        t1.TampilJenis();
        t1.TampilDim();

        Console.WriteLine("Luas adalah " + t1.Luas());
        Console.WriteLine();

        Console.WriteLine("Info untuk t2: ");
        t2.TampilJenis();
        t2.TampilDim();

        Console.WriteLine("Luas adalah " + t2.Luas());
    }
}

Di sini, konstruktor pada Segitiga menginisialisasi anggota-anggota dari BangunDuaD yang diwarisi berikut dengan bidang Jenis miliknya sendiri.

Ketika kedua kelas basis dan kelas terderivasi mendefinisikan konstruktor, prosesnya menjadi sedikit lebih kompleks karena konstruktor pada kedua kelas basis dan kelas terderivasi harus dieksekusi. Pada kasus ini, Anda perlu menggunakan katakunci C# yang lain, base, yang memiliki dua kegunaan. Kegunaan pertama adalah memanggil konstruktor pada kelas basis. Kegunaan kedua adalah untuk mengakses anggota kelas basis yang tidak dikenali oleh anggota kelas terderivasi.


Memanggil Konstruktor Kelas Basis
Sebuah kelas terderivasi dapat memanggil konstruktor yang didefinisikan di dalam kelas basisnya menggunakan format terekspansi dari deklarasi konstruktor kelas terderivasi dan katakunci base. Bentuk umum dari deklarasi ini ditampilkan sebagai berikut:

konstruktor-terderivasi(daftar-param) : base(daftar-arg) {
  // tubuh konstruktor
}

Di sini, daftar-arg menspesifikasi sembarang argumen yang dibutuhkan konstruktor pada kelas basis. Perhatikan penempatan titik-dua.

Untuk melihat bagaimana base digunakan, perhatikan versi dari BangunDuaD pada program berikut. Program mendefinisikan sebuah konstruktor yang menginisialisasi properti Lebar dan Tinggi. Konsturktor ini kemudian dipanggil oleh konstruktor Segitiga.

// Menambahkan sebuah konstruktor pada BangunDuaD.
using System;

// Sebuah kelas untuk objek dua dimensi.
class BangunDuaD
{
    double lebar_bangun; // sekarang private
    double tinggi_bangun; // sekarang private

    // Konstruktor untuk BangunDuaD.
    public BangunDuaD(double w, double h) {
      Lebar = w;
      Tinggi = h;
    }
   
    // Properti untuk Lebar dan Tinggi.
    public double Lebar
    {
        get { return lebar_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public double Tinggi
    {
        get { return tinggi_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public void TampilDim()
    {
        Console.WriteLine("Lebar dan tinggi adalah " +
                          Lebar + " dan " + Tinggi);
    }
}

// Sebuah kelas terderivasi dari BangunDuaD untuk segitiga.
class Segitiga : BangunDuaD
{
    string Jenis; // jenis segitiga

    // Memanggil konstruktor kelas basis.
    public Segitiga(string s, double w, double h) : base(w, h) {
      Jenis = s;
    }
   
    // Menghasilkan luas segitiga.
    public double Luas()
    {
        return Lebar * Tinggi / 2;
    }

    // Menampilkan jenis segitiga.
    public void TampilJenis()
    {
        Console.WriteLine("Segitiga adalah " + Jenis);
    }
}

class Bangun4
{
    static void Main()
    {
        Segitiga t1 = new Segitiga("sama-sisi", 4.0, 4.0);
        Segitiga t2 = new Segitiga("tak sama-sisi", 8.0, 12.0);

        Console.WriteLine("Info untuk t1: ");
        t1.TampilJenis();
        t1.TampilDim();

        Console.WriteLine("Luas adalah " + t1.Luas());
        Console.WriteLine();

        Console.WriteLine("Info untuk t2: ");
        t2.TampilJenis();
        t2.TampilDim();

        Console.WriteLine("Luas adalah " + t2.Luas());
    }
}

Perhatikan bahwa konstruktor Segitiga sekarang dideklarasikan seperti ini:

public Segitiga(string s, double w, double h) : base(w, h) {

Pada versi ini, Segitiga() memanggil base dengan parameter w dan h. Ini menyebabkan konstruktor BangunDuaD() dipanggil, yang menginisialisasi Lebar dan Tinggi menggunakan kedua nilai tersebut. Segitiga tidak lagi menginisialisasi kedua nilai itu. Konstruktor Segitiga hanya perlu menginisialisasi nilai Jenis.

Sembarang konstruktor yang didefinisikan oleh kelas basis dapat dipanggil dengan base. Konstruktor yang dieksekusi adalah yang cocok dengan argumen. Sebagai contoh, berikut adalah versi terekspansi dari BangunDuaD dan Segitiga, yang menyertakan konstruktor default dan konstruktor yang mengambil satu argumen.

// Menambahkan konstruktor lagi pada BangunDuaD.
using System;

// Sebuah kelas untuk objek dua dimensi.
class BangunDuaD
{
    double lebar_bangun; // sekarang private
    double tinggi_bangun; // sekarang private

    // KOnstruktor default.
    public BangunDuaD() {
      Lebar = Tinggi = 0.0;
    }
   
    // Membangun objek dengan lebar dan tinggi sama.
    public BangunDuaD(double x) {
      Lebar = Tinggi = x;
    }

    // Konstruktor untuk BangunDuaD.
    public BangunDuaD(double w, double h) {
      Lebar = w;
      Tinggi = h;
    }
   
    // Properti untuk Lebar dan Tinggi.
    public double Lebar
    {
        get { return lebar_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public double Tinggi
    {
        get { return tinggi_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public void TampilDim()
    {
        Console.WriteLine("Lebar dan tinggi adalah " +
                          Lebar + " dan " + Tinggi);
    }
}

// Sebuah kelas terderivasi dari BangunDuaD untuk segitiga.
class Segitiga : BangunDuaD
{
    string Jenis; // jenis segitiga

    /* Konstruktor default, secara otomatis memanggil
       konstruktor default pada BangunDuaD. */
    public Segitiga() {
      Jenis = "null";
    }
   
    // Memanggil konstruktor kelas basis.
    public Segitiga(string s, double w, double h) : base(w, h) {
      Jenis = s;
    }
   
    // Membangun segitiga sama-sisi.
    public Segitiga(double x) : base(x) {
      Jenis = "sama-sisi";
    }

    // Menghasilkan luas segitiga.
    public double Luas()
    {
        return Lebar * Tinggi / 2;
    }

    // Menampilkan jenis segitiga.
    public void TampilJenis()
    {
        Console.WriteLine("Segitiga adalah " + Jenis);
    }
}

class Bangun5
{
    static void Main()
    {
        Segitiga t1 = new Segitiga();
        Segitiga t2 = new Segitiga("tak sama-sisi", 8.0, 12.0);
        Segitiga t3 = new Segitiga(4.0);

        t1 = t2;

        Console.WriteLine("Info untuk t1: ");
        t1.TampilJenis();
        t1.TampilDim();

        Console.WriteLine("Luas adalah " + t1.Luas());
        Console.WriteLine();

        Console.WriteLine("Info untuk t2: ");
        t2.TampilJenis();
        t2.TampilDim();

        Console.WriteLine("Luas adalah " + t2.Luas());

        Console.WriteLine("Info untuk t3: ");
        t3.TampilJenis();
        t3.TampilDim();

        Console.WriteLine("Luas adalah " + t3.Luas());
    }
}

Berikut adalah keluaran program:

Info untuk t1:
Segitiga adalah tak sama-sisi
Lebar dan tinggi adalah 0 dan 12
Luas adalah 0

Info untuk t2:
Segitiga adalah tak sama-sisi
Lebar dan tinggi adalah 0 dan 12
Luas adalah 0
Info untuk t3:
Segitiga adalah sama-sisi
Lebar dan tinggi adalah 0 dan 4
Luas adalah 0


Pewarisan Dan Penyembunyian Nama
Dimungkinkan bagi kelas terderivasi untuk mendefinisikan sebuah anggota, yang memiliki nama sama dengan suatu anggota di dalam kelas basisnya. Ketika ini terjadi, anggota di dalam kelas basis tidak dikenali di dalam kelas terderivasi. Meskipun ini bukan error dalam C#, tapi kompiler tetap akan mengeluarkan pesan peringatan. Pesan tersebut dimaksudkan untuk mengingatkan Anda akan fakta bahwa terdapat sebuah nama yang sedang tersembunyi. Jika maksud Anda adalah untuk menyembunyikan sebuah anggota kelas basis, maka untuk mencegah pesan peringatan, anggota kelas terderivasi harus diawali dengan katakunci new. Anda perlu memahami bahwa penggunaan new berbeda dan terpisah dari penggunaannya ketika menciptakan sebuah objek instans.

Berikut adalah sebuah contoh penyembunyian nama:

// Sebuah contoh penyembunyian nama, terkait dengan pewarisan.
using System;

class A
{
    public int i = 0;
}

// Menciptakan kelas terderivasi.
class B : A
{
    new int i; // i ini menyembunyikan i di dalam A

    public B(int b)
    {
        i = b; // i di dalam B
    }

    public void Tampil()
    {
        Console.WriteLine("i di dalam kelas terderivasi: " + i);
    }
}

class PenyembunyianNama
{
    static void Main()
    {
        B ob = new B(2);
        ob.Tampil();
    }
}

Pertama, perhatikan penggunaan new pada baris ini:

new int i;  // i ini menyembunyikan i di dalam A

Intinya, statemen tersebut memberitahu kompiler bahwa Anda mengetahui bahwa sebuah variabel baru, bernama i, sedang diciptakan untuk menyembunyikan i di dalam kelas basis A. Jika Anda tidak membubuhkan new, maka pesan peringatan akan dibangkitkan.

Keluaran program ditampilkan di sini:

i di dalam kelas terderivasi: 2

Karena B mendefinisikan variabel instansnya sendiri, yang dinamakan i, ia menyembunyikan i di dalam A. Oleh karena itu, ketika Tampil() dipanggil pada objek bertipe B, nilai i yang didefinisikan oleh B ditampilkan, bukan i yang didefinisikan di dalam A.


Menggunakan base Untuk Mengakses Nama Tersembunyi
Berikut adalah format kedua dar base yang berperan seperti this, dengan pengecualian bahwa base dipakai untuk menunjuk ke kelas basis dari suatu kelas terderivasi ketika digunakan. Penggunaan ini mempunyai bentuk umum berikut:

base.anggota

Di sini, anggota dapat berupa sebuah metode atau suatu variabel instans. Penggunaan base pada kasus ini merupakan yang paling aplikatif. Perhatikan versi hirarki pewarisan dari contoh sebelumnya:

// Menggunakan base untuk mengatasi penyembunyian nama.
using System;

class A
{
    public int i = 0;
}

// Menciptakan kelas terderivasi.
class B : A
{
    new int i; // i ini menyembunyikan i di dalam A

    public B(int a, int b)
    {
        base.i = a; // ini mengakses i di dalam A
        i = b; // i di dalam B
    }

    public void Tampil()
    {
        // Ini menampilkan i di dalam A.
        Console.WriteLine("i di dalam kelas basis: " + base.i);
       
        // Ini menampilkan i di dalam B.
        Console.WriteLine("i di dalam kelas terderivasi: " + i);
    }
}

class MengekstrakNama
{
    static void Main()
    {
        B ob = new B(1, 2);

        ob.Tampil();
    }
}

Ini menghasilkan keluaran program:

i di dalam kelas basis: 1
i di dalam kelas terderivasi: 2

Meskipun variabel instans i di dalam B menyembunyikan i di dalam A, base memampukan akses terhadap i yang didefinisikan di dalam kelas basis.

Metode tersembunyi juga dapat dipanggil melalui penggunaan base. Sebagai contoh, dalam kode berikut, kelas B mewarisi kelas A, dan kedua A dan B mendeklarasikan sebuah metode, yang bernama Tampil(). Di dalam metode Tampil() pada kelas B, versi Tampil() yang didefinisikan oleh A dipanggil melalui penggunaan base.

// Memanggil metode tersembunyi.
using System;

class A
{
    public int i = 0;

    // Tampil() di dalam A
    public void Tampil()
    {
        Console.WriteLine("i di dalam kelas basis: " + i);
    }
}

// Menciptakan kelas terderivasi.
class B : A
{
    new int i; // i ini menyembunyikan i di dalam A

    public B(int a, int b)
    {
        base.i = a; // ini mengakses i di dalam A
        i = b; // i di dalam B
    }

    // Ini menyembunyikan Tampil() di dalam A. Perhatikan penggunaan new.
    new public void Tampil()
    {
        base.Tampil(); // ini memanggil Tampil() di dalam A
       
        // ini menampilkan i di dalam B
        Console.WriteLine("i di dalam kelas terderivasi: " + i);
    }
}

class MengekstrakNama
{
    static void Main()
    {
        B ob = new B(1, 2);

        ob.Tampil();
    }
}

Keluaran program ditampilkan di sini:

i di dalam kelas basis: 1
i di dalam kelas terderivasi: 2

Seperti yang dapat Anda lihat, base.Tampil() memanggil versi dari Tampil() pada kelas basis. Satu hal penting lain: Perhatikan bahwa new digunakan di dalam program tersebut untuk memberitahu kompiler bahwa Anda mengetahui sebuah metode baru, yang dinamakan Tampil(), sedang dideklarasikan dan menyembunyikan Tampil() di dalam A.


Menciptakan Hirarki Multilevel
Sampai sejauh ini, Anda telah menggunakan hirarki kelas sederhana, yang hanya memuat satu kelas basis dan satu kelas terderivasi. Tetapi, Anda dapat membangun hirarki pewarisan, yang memuat sebanyak mungkin lapisan pewarisan yang Anda inginkan. Seperti disebutkan sebelumnya, adalah hal legal untuk menggunakan sebuah kelas terderivasi sebagai kelas basis bagi kelas lain. Sebagai contoh, diberikan tiga kelas, sebut saja A, B, dan C, dimana C diderivasi dari B, dan B diderivasi dari A. Ketika situasi semacam ini terjadi, masing-masing kelas terderivasi mewarisi semua yang ditemukan di dalam kelas basisnya. Pada kasus ini, C mewarisi semua aspek dari B dan A.

Untuk melihat bagaimana hirarki multilevel digunakan, perhatikan program berikut. Di dalam program, kelas terderivasi Segitiga dipakai kelas basis untuk menciptakan kelas terderivasi, SegitigaWarna. Kelas SegitigaWarna mewarisi semua aspek dari Segitiga dan BangunDuaD dan menambahkan sebuah bidang, warna, yang memuat warna segitiga.

// Hirarki pewarisan multilevel.
using System;

// Sebuah kelas untuk objek dua dimensi.
class BangunDuaD
{
    double lebar_bangun; // sekarang private
    double tinggi_bangun; // sekarang private

    // Konstruktor default.
    public BangunDuaD()
    {
        Lebar = Tinggi = 0.0;
    }

    // Membangun objek dengan lebar dan tinggi sama.
    public BangunDuaD(double x)
    {
        Lebar = Tinggi = x;
    }

    // Konstruktor untuk BangunDuaD.
    public BangunDuaD(double w, double h)
    {
        Lebar = w;
        Tinggi = h;
    }

    // Properti untuk Lebar dan Tinggi.
    public double Lebar
    {
        get { return lebar_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public double Tinggi
    {
        get { return tinggi_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public void TampilDim()
    {
        Console.WriteLine("Lebar dan tinggi adalah " +
                          Lebar + " dan " + Tinggi);
    }
}

// Sebuah kelas terderivasi dari BangunDuaD untuk segitiga.
class Segitiga : BangunDuaD
{
    string Jenis; // private

    /* Konstruktor default, secara otomatis memanggil
       konstruktor default pada BangunDuaD. */
    public Segitiga()
    {
        Jenis = "null";
    }

    // Memanggil konstruktor kelas basis.
    public Segitiga(string s, double w, double h)
        : base(w, h)
    {
        Jenis = s;
    }

    // Membangun segitiga sama-sisi.
    public Segitiga(double x)
        : base(x)
    {
        Jenis = "sama-sisi";
    }

    // Menghasilkan luas segitiga.
    public double Luas()
    {
        return Lebar * Tinggi / 2;
    }

    // Menampilkan jenis segitiga.
    public void TampilJenis()
    {
        Console.WriteLine("Segitiga adalah " + Jenis);
    }
}

// Mewarisi Segitiga.
class SegitigaWarna : Segitiga
{
    string warna;

    public SegitigaWarna(string c, string s, double w, double h) : base(s, w, h) {
        warna = c;
    }

    // Menampilkan warna.
    public void TampilWarna()
    {
        Console.WriteLine("Warnanya " + warna);
    }
}

class Bangun6
{
    static void Main()
    {
        SegitigaWarna t1 =
            new SegitigaWarna("Biru", "tak sama-sisi", 8.0, 12.0);

        SegitigaWarna t2 =
            new SegitigaWarna("Merah", "sama-sisi", 2.0, 2.0);

        Console.WriteLine("Info untuk t1: ");
        t1.TampilJenis();
        t1.TampilDim();
        t1.TampilWarna();

        Console.WriteLine("Luas adalah " + t1.Luas());
        Console.WriteLine();

        Console.WriteLine("Info untuk t2: ");
        t2.TampilJenis();
        t2.TampilDim();
        t2.TampilWarna();

        Console.WriteLine("Luas adalah " + t2.Luas());
    }
}

Keluaran program ditampilkan di sini:

Info untuk t1:
Segitiga adalah tak sama-sisi
Lebar dan tinggi adalah 0 dan 12
Warnanya Biru
Luas adalah 0

Info untuk t2:
Segitiga adalah sama-sisi
Lebar dan tinggi adalah 0 dan 2
Warnanya Merah
Luas adalah 0

Karena pewarisan, SegitigaWarna dapat memanfaatkan kelas Segitiga dan BangunDuaD. Inilah kekuatan pewarisan, Anda bisa mendaur-ulang atau memakai kembali kode di kelas lain.

Contoh ini mengilustrasikan satu hal penting lain: base selalu merujuk ke konstruktor pada kelas basis terdekat. base di dalam SegitigaWarna memanggil konstruktor pada Segitiga. base di dalam Segitiga memanggil konstruktor pada BangunDuaD. Pada hirarki pewarisan, jika konstruktor kelas basis memerlukan parameter, maka semua kelas terderivasi harus melewatkan parameter tersebut.


Kapan Konstruktor Dipanggil?
Dalam diskusi mengenai pewarisan dan hirarki pewarisan, ada satu pertanyaan penting: Ketika sebuah objek kelas terderivasi diciptakan, konstruktor siapa yang dieksekusi lebih dahulu? Konstruktor pada kelas terderivasi atau konstruktor yang didefinisikan oleh kelas basis? Misalnya, diberikan sebuah kelas terderivasi B dan kelas basis A. Pertanyaannya adalah konstruktor A dipanggil sebelum konstruktor B, atau sebaliknya? Jawabannya adalah bahwa di dalam hirarki pewarisan, konstruktor dipanggil sesuai urutan derivasi, mulai dari kelas basis sampai kelas terderivasi. Di samping itu, urutan ini berlaku tanpa memandang base dipakai atau tidak. Jika base digunakan, maka konstruktor default (tanpa parameter) pada tiap kelas basis akan dieksekusi. Program berikut mengilustrasikan urutan pengeksekusian konstruktor:

// Demonstrasi kapan konstruktor dipanggil.
using System;

// Menciptakan sebuah kelas basis.
class A {
  public A() {
    Console.WriteLine("Menciptakan A.");
  }
}

// Menciptakan sebuah kelas diderivasi dari A.
class B : A {
  public B() {
      Console.WriteLine("Menciptakan B.");
  }
}

// Menciptakan sebuah kelas diderivasi dari B.
class C : B
{
    public C() {
        Console.WriteLine("Menciptakan C.");
    }
}

class UrutanPenciptaan {
    static void Main() {
        C c = new C();
    }
}

Keluaran program ditampilkan di sini:

Menciptakan A.
Menciptakan B.
Menciptakan C.

Seperti yang dapat Anda lihat, konstruktor dipanggil dengan urutan derivasi. Jika Anda memikirkannya, maka menjadi masuk akal bila konstruktor dieksekusi dengan urutan derivasi. Karena kelas basis tidak memiliki pengetahuan tentang kelas terderivasi, sembarang inisialisasi yang dibutuhkan kelas basis terpisah dan independen dari inisialisasi yang diperlukan kelas terderivasi. Oleh karena itu, konstruktor kelas basis dieksekusi lebih dahulu.


Referensi Kelas Basis Dan Objek Terderivasi
Seperti yang Anda ketahui, C# sangat menekankan tipe data. Di samping konversi standar dan promosi otomatis yang diterapkan pada tipe nilai, kompatibilitas tipe sangat ditegakkan dalam C#. Oleh karena itu, variabel referensi untuk suatu tipe kelas secara normal tidak dapat menunjuk ke objek bertipe kelas lain. Sebagai contoh, perhatikan program berikut yang mendeklarasikan dua kelas yang identik:

// Program ini tidak akan bisa dikompilasi.
class X {
  int a;

  public X(int i) { a = i; }
}

class Y {
  int a;

  public Y(int i) { a = i; }
}

class RefTakKompatibel {
  static void Main() {
    X x2;
    Y y = new Y(5);

    x2 = x; // OK, keduanya bertipe sama

    x2 = y; // Error, tidak bertipe sama
  }
}

Di sini, meskipun kelas X dan kelas Y secara fisik sama, tidak dimungkinkan untuk menugaskan referensi bertipe Y untuk menunjuk ke sebuah variabel bertipe X karena keduanya berbeda tipe. Oleh karena itu, baris ini salah karena menyebabkan error kompilasi:

x2 = y; // Error, tidak bertipe sama

Secara umum, sebuah variabel referensi objek hanya dapat menunjuk ke objek bertipe sama.

Namun, ada satu pengecualian penting dalam C#, pada kasus ini. Variabel referensi kelas basis dapat ditugasi referensi yang menunjuk ke sebuah objek kelas terderivasi (yang diderivasi dari kelas basis tersebut). Hal ini legal karena sebuah instans bertipe terderivasi mengenkapsulasi instans bertipe basis. Berikut diberikan sebuah contoh yang mengilustrasikan gagasan ini:

// Program ini tidak akan bisa dikompilasi.
using System;

class X {
  public int a;

  public X(int i) { a = i; }
}

class Y : X {
  int b;

  public Y(int i, int j) : base(j) {
    b = i;
  }
}

class RefBasis {
  static void Main() {
    X x = new X(10);
    X x2;
    Y y = new Y(5, 6);

    x2 = x; // OK, keduanya bertipe sama
    Console.WriteLine("x2.a: " + x2.a);

    x2 = y; // OK karena Y diderivasi dari X
    Console.WriteLine("x2.a: " + x2.a);

    // referensi X hanya mengetahui tentang anggota X
    x2.a = 19; // OK
   
    // x2.b = 27; // Error, X tidak memiliki anggota b
  }
}

Pada program ini, Y diderivasi dari X. Sekarang, penugasan

x2 = y; // OK karena Y diderivasi dari X

diijinkan karena referensi kelas basis, x2 pada kasus ini, dapat menunjuk ke objek kelas terderivasi (yang merupakan objek yang ditunjuk oleh y).

Hal penting untuk memahami bahwa ketika referensi yang menunjuk ke objek kelas terderivasi ditugaskan kepada variabel referensi kelas basis, Anda hanya dapat mengakses bagian dari objek yang didefinisikan oleh kelas basis. Inilah alasan mengapa x2 tidak dapat mengakses b meski it menunjuk ke objek Y. Hal ini masuk akal karena kelas basis tidak mengetahui apapun tentang aspek baru yang ditambahkan ke dalam kelas terderivasi.

Seperti yang Anda ketahui, umum dijumpai sebuah kelas yang mendefinisikan konstruktor yang mengambil objek dari kelasnya sebagai parameter. Ini menyebabkan kelas tersebut menciptakan salinan dari sebuah objek. Kelas yang diderivasi dari kelas semacam itu dapat memanfaatkan situasi tersebut. Sebagai contoh, perhatikan versi terkini dari BangunDuaD dan Segitiga. Kedua kelas itu menambahkan konstruktor yang mengambil sebuah objek sebagai parameter.

// Melewatkan referensi kelas terderivasi kepada referensi kelas basis.
using System;

// Sebuah kelas untuk objek dua dimensi.
class BangunDuaD
{
    double lebar_bangun; // sekarang private
    double tinggi_bangun; // sekarang private

    // KOnstruktor default.
    public BangunDuaD()
    {
        Lebar = Tinggi = 0.0;
    }

    // Constructor for TwoDShape.
    public BangunDuaD(double w, double h)
    {
      Lebar = w;
      Tinggi = h;
    }

    // Membangun objek dengan lebar dan tinggi sama.
    public BangunDuaD(double x)
    {
        Lebar = Tinggi = x;
    }

    // Construct a copy of a TwoDShape object.
    public BangunDuaD(BangunDuaD ob)
    {
      Lebar = ob.Lebar;
      Tinggi = ob.Tinggi;
    }

    // Properti untuk Lebar dan Tinggi.
    public double Lebar
    {
        get { return lebar_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public double Tinggi
    {
        get { return tinggi_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public void TampilDim()
    {
        Console.WriteLine("Lebar dan tinggi adalah " +
                          Lebar + " dan " + Tinggi);
    }
}

// Sebuah kelas terderivasi dari BangunDuaD untuk segitiga.
class Segitiga : BangunDuaD
{
    string Jenis; // jenis segitiga

    /* Konstruktor default, secara otomatis memanggil
       konstruktor default pada BangunDuaD. */
    public Segitiga()
    {
        Jenis = "null";
    }

    // Memanggil konstruktor kelas basis.
    public Segitiga(string s, double w, double h) : base(w, h)
    {
        Jenis = s;
    }

    // Membangun segitiga sama-sisi.
    public Segitiga(double x) : base(x)
    {
        Jenis = "sama-sisi";
    }

    // Construct a copy of a Triangle object.
    public Segitiga(Segitiga ob) : base(ob)
    {
      Jenis = ob.Jenis;
    }

    // Menghasilkan luas segitiga.
    public double Luas()
    {
        return Lebar * Tinggi / 2;
    }

    // Menampilkan jenis segitiga.
    public void TampilJenis()
    {
        Console.WriteLine("Segitiga adalah " + Jenis);
    }
}

class Bangun7
{
    static void Main()
    {
        Segitiga t1 = new Segitiga("tak sama-sisi", 8.0, 12.0);

        // Membuat salinan dari t1.
        Segitiga t2 = new Segitiga(t1);

        Console.WriteLine("Info untuk t1: ");
        t1.TampilJenis();
        t1.TampilDim();

        Console.WriteLine("Luas adalah " + t1.Luas());
        Console.WriteLine();

        Console.WriteLine("Info untuk t2: ");
        t2.TampilJenis();
        t2.TampilDim();

        Console.WriteLine("Luas adalah " + t2.Luas());
    }
}

Pada program ini, t2 dikonstruksi dari t1, jadi keduanya identik. Keluaran program ditampilkan di sini:

Info untuk t1:
Segitiga adalah tak sama-sisi
Lebar dan tinggi adalah 0 dan 12
Luas adalah 0

Info untuk t2:
Segitiga adalah tak sama-sisi
Lebar dan tinggi adalah 0 dan 12
Luas adalah 0

Perhatikan secara khusus pada konstruktor Segitiga ini:

public Segitiga(Segitiga ob) : base(ob) {
  Jenis = ob.Jenis;
}
Konstruktor itu menerima sebuah objek bertipe Segitiga, dan melewatkan objek tersebut (melalui base) kepada konstruktor BangunDuaD ini:

public BangunDuaD(BangunDuaD ob) {
  Lebar = ob.Lebar;
  Tinggi = ob.Tinggi;
}

Kunci pentingnya adalah bahwa BangunDuaD() mengharapkan sebuah objek BangunDuaD. Tetapi, Segitiga() melewatkan sebuah objek Segitiga kepada BangunDuaD. Seperti dijelaskan sebelumnya, hal ini bisa dilakukan karena referensi kelas basis dapat menunjuk ke objek kelas terderivasi. Jadi, pelewatan sebuah referensi ke objek terderivasi (dari BangunDuaD) kepada BangunDuaD() dapat dilakukan. Ingat bahwa konstruktor BangunDuaD() hanya menginisialisasi bagian dari objek kelas terderivasi yang merupakan anggota BangunDuaD.


Metode Virtual Dan Overriding
Metode virtual adalah sebuah metode yang dideklarasikan sebagai virtual di dalam kelas basis. Karakteristik dari sebuah metode virtual adalah bahwa ia dapat didefinisikan-ulang di dalam satu atau lebih kelas terderivasi. Jadi, setiap kelas terderivasi bisa memiliki versi metode virtualnya sendiri.

Anda mendeklarasikan sebuah metode sebagai virtual di dalam kelas basis dengan membubuhkan katakunci virtual di depan deklarasi. Ketika metode virtual didefinisikan-ulang oleh kelas terderivasi, pemodifikasi override digunakan. Jadi, proses pendefinisian-ulang sebuah metode virtual dikenal dengan method overriding. Ketika mendefinisikan-ulang sebuah metode, maka nama, tipe nilai balik, dan sidik metode pendefinisi-ulang harus sama dengan metode virtual. Di samping itu, metode virtual tidak bisa dispesifikasi sebagai static atau abstract. Berikut disajikan sebuah contoh pendefinisian-ulang metode:

// Demonstrasi metode virtual.
using System;

class Basis
{
    // Menciptakan metode virtual di dalam kelas basis.
    public virtual void Siapa()
    {
        Console.WriteLine("Siapa() di dalam Basis");
    }
}

class Terderivasi1 : Basis
{
    // Mendefinisikan-ulang Siapa() di dalam kelas terderivasi.
    public override void Siapa()
    {
        Console.WriteLine("Siapa() di dalam Terderivasi1");
    }
}

class Terderivasi2 : Basis
{
    // Mendefinisikan-ulang lagi Siapa() di dalam kelas terderivasi.
    public override void Siapa()
    {
        Console.WriteLine("Siapa() di dalam Terderivasi2");
    }
}

class DemoOverride
{
    static void Main()
    {
        Basis basisOb = new Basis();
        Terderivasi1 dOb1 = new Terderivasi1();
        Terderivasi2 dOb2 = new Terderivasi2();

        Basis refBasis; // sebuah referensi kelas basis

        refBasis = basisOb;
        refBasis.Siapa();

        refBasis = dOb1;
        refBasis.Siapa();

        refBasis = dOb2;
        refBasis.Siapa();
    }
}

Keluaran program ditampilkan di sini:

Siapa() di dalam Basis
Siapa() di dalam Terderivasi1
Siapa() di dalam Terderivasi2

Program ini menciptakan sebuah kelas basis, bernama Basis, dan dua kelas terderivasi, bernama Terderivasi1 dan Terderivasi2. Kelas Basis mendeklarasikan sebuah metode, Siapa(), dan dua kelas terderivasi mendefinisikannya-ulang. Di dalam metode Main(), objek bertipe Basis, Terderivasi1, dan Terderivasi2 dideklarasikan. Di samping itu, sebuah referensi bertipe Basis, yang dinamakan refBasis, juga dideklarasikan. Program kemudian menugaskan sebuah referensi yang menunjuk ke tiap tipe objek kepada refBasis dan menggunakan referensi tersebut untuk memanggil Siapa(). Seperti yang ditampilkan pada keluaran program, versi mana dari Siapa() yang dieksekusi ditentukan oleh tipe objek yang sedang ditunjuk pada saat pemanggilan, bukan ditentukan oleh tipe kelas dari refBasis.

Jika kelas terderivasi tidak menyediakan versi sendiri dari sebuah metode virtual, maka yang digunakan adalah yang ada di dalam kelas basis. Sebagai contoh:

/* Ketika metode virtual tidak didefinisikan-ulang,
   metode kelas basis yang digunakan. */
using System;

class Basis
{
    // Menciptakan metode virtual di dalam kelas basis.
    public virtual void Siapa()
    {
        Console.WriteLine("Siapa() di dalam Basis");
    }
}

class Terderivasi1 : Basis
{
    // Mendefinisikan-ulang Siapa() di dalam kelas terderivasi.
    public override void Siapa()
    {
        Console.WriteLine("Siapa() di dalam Terderivasi1");
    }
}

class Terderivasi2 : Basis
{
    // Kelas ini tidak mendefinisikan-ulang Siapa()
}

class DemoOverride2
{
    static void Main()
    {
        Basis basisOb = new Basis();
        Terderivasi1 dOb1 = new Terderivasi1();
        Terderivasi2 dOb2 = new Terderivasi2();

        Basis refBasis; // sebuah referensi kelas basis

        refBasis = basisOb;
        refBasis.Siapa();

        refBasis = dOb1;
        refBasis.Siapa();

        refBasis = dOb2;
        refBasis.Siapa(); // memanggil Siapa() pada kelas basis
    }
}

Keluaran program ditampilkan di sini:

Siapa() di dalam Basis
Siapa() di dalam Terderivasi1
Siapa() di dalam Basis

Di sini, Terderivasi2 tidak mendefinisikan-ulang Siapa(). Jadi, ketika Siapa() dipanggil pada sebuah objek Terderivasi2, metode Siapa() di dalam Basis dieksekusi.

Pada kasus hirarki pewarisan multilevel, jika sebuah kelas terderivasi tidak mendefinisikan-ulang metode virtual, maka, ketika bergerak ke atas hirarki, metode yang pertama-kali mendefinisikan-ulang metode virtual tersebutlah yang dieksekusi. Sebagai contoh:

/* Pada kasus hirarki pewarisan multilevel,
   metode yang pertama-kali mendefinisikan-ulang metode
   virtual tersebutlah yang dieksekusi. */
using System;

class Basis
{
    // Menciptakan metode virtual di dalam kelas basis.
    public virtual void Siapa()
    {
        Console.WriteLine("Siapa() di dalam Basis");
    }
}

class Terderivasi1 : Basis
{
    // Mendefinisikan-ulang Siapa() di dalam kelas terderivasi.
    public override void Siapa()
    {
        Console.WriteLine("Siapa() di dalam Terderivasi1");
    }
}

class Terderivasi2 : Terderivasi1
{
    // Kelas ini tidak mendefinisikan-ulang Siapa()
}

class Terderivasi3 : Terderivasi2
{
    // Kelas ini tidak mendefinisikan-ulang Siapa()
}

class DemoOverride3
{
    static void Main()
    {
        Terderivasi3 dOb = new Terderivasi3();
        Basis refBasis; // sebuah referensi kelas basis

        refBasis = dOb;
        refBasis.Siapa(); // memanggil Siapa() pada kelas Terderivasi1
    }
}

Keluaran program ditampilkan di sini:

Siapa() di dalam Terderivasi1

Di sini, Terderivasi3 mewarisi Terderivasi2, yang mewarisi Terderivasi1, yang mewarisi Basis. Seperti ditegaskan pada keluaran program, karena Siapa() tidak didefinisikan-ulang baik oleh Terderivasi3 maupun oleh Terderivasi2, maka Siapa() yang didefinisikan-ulang di dalam Terderivasi1 dieksekusi (versi pertama dari Siapa() yang ditemukan dalam hirarki pewarisan dari bawah ke atas).

Satu hal penting lain. Properti juga dapat dimodifikasi dengan katakunci virtual dan didefinisikan-ulang menggunakan override. Hal ini berlaku pula untuk indekser.

Menerapkan Metode Virtual
Untuk lebih memahami keunggulan dari metode virtual, sekarang akan diterapkan pada kelas BangunDuaD. Pada beberapa contoh sebelumnya, setiap kelas yang diderivasi dari BangunDuaD mendefinisikan sebuah metode bernama Luas(). Ini mengimplikasikan bahwa adalah lebih baik untuk membuat Luas() sebagai metode virtual pada kelas BangunDuaD, sehingga membolehkan setiap kelas terderivasi mendefinisikannya-ulang bergantung pada jenis bangun spesifik. Program berikut melakukan hal ini.

// Menggunakan metode virtual dan polimorfisme.
using System;

// Sebuah kelas untuk objek dua dimensi.
class BangunDuaD
{
    double lebar_bangun; // sekarang private
    double tinggi_bangun; // sekarang private

    // KOnstruktor default.
    public BangunDuaD()
    {
        Lebar = Tinggi = 0.0;
        nama = "null";
    }

    // Membangun objek dengan lebar dan tinggi sama.
    public BangunDuaD(double x, string n)
    {
        Lebar = Tinggi = x;
        nama = n;
    }

    // Konstruktor terparameterisasi
    public BangunDuaD(double w, double h, string n)
    {
        Lebar = w;
        Tinggi = h;
        nama = n;
    }

    // Menciptakan sebuah salinan dari objek BangunDuaD.
    public BangunDuaD(BangunDuaD ob)
    {
      Lebar = ob.Lebar;
      Tinggi = ob.Tinggi;
      nama = ob.nama;
    }

    // Properti untuk Lebar dan Tinggi.
    public double Lebar
    {
        get { return lebar_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public double Tinggi
    {
        get { return tinggi_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public string nama { get; set; }

    public void TampilDim()
    {
        Console.WriteLine("Lebar dan tinggi adalah " +
                          Lebar + " dan " + Tinggi);
    }

    public virtual double Luas()
    {
        Console.WriteLine("Luas() harus didefinisikan-ulang");
        return 0.0;
    }
}

// Sebuah kelas terderivasi dari BangunDuaD untuk segitiga.
class Segitiga : BangunDuaD
{
    string Jenis; // jenis segitiga

    /* Konstruktor default, secara otomatis memanggil
       konstruktor default pada BangunDuaD. */
    public Segitiga()
    {
        Jenis = "null";
    }

    // Konstruktor untuk Segitiga.
    public Segitiga(string s, double w, double h)
        : base(w, h, "segitiga")
    {
        Jenis = s;
    }

    // Membangun segitiga sama-sisi.
    public Segitiga(double x) : base(x, "segitiga")
    {
        Jenis = "sama-sisi";
    }

    // Menciptakan sebuah salinan dari objek Segitiga.
    public Segitiga(Segitiga ob) : base(ob)
    {
        Jenis = ob.Jenis;
    }

    // Mendefinisikan-ulang Luas() untuk Segitiga.
    public override double Luas()
    {
        return Lebar * Tinggi / 2;
    }

    // Menampilkan jenis segitiga.
    public void TampilJenis()
    {
        Console.WriteLine("Segitiga adalah " + Jenis);
    }
}

// Sebuah kelas terderivasi dari BangunDuaD untuk persegipanjang.
class PersegiPanjang : BangunDuaD {
    // Konstruktor untuk PersegiPanjang.
    public PersegiPanjang(double w, double h) :
      base(w, h, "persegipanjang"){ }

    // Membangun sebuah persegi.
    public PersegiPanjang(double x) :
      base(x, "persegipanjang") { }

    // Menciptakan sebuah salinan dari objek PersegiPanjang.
    public PersegiPanjang(PersegiPanjang ob) : base(ob) { }

    // Menghasilkan true jika persegipanjang adalah persegi.
    public bool ApaPersegi()
    {
      if (Lebar == Tinggi) return true;
      return false;
    }

    // Mendefinisikan-ulang Luas() untuk PersegiPanjang.
    public override double Luas()
    {
      return Lebar * Tinggi;
    }
}

class BangunDinamis
{
    static void Main()
    {
        BangunDuaD[] bangun = new BangunDuaD[5];

        bangun[0] = new Segitiga("sama-sisi", 8.0, 12.0);
        bangun[1] = new PersegiPanjang(10);
        bangun[2] = new PersegiPanjang(10, 4);
        bangun[3] = new Segitiga(7.0);
        bangun[4] = new BangunDuaD(10, 20, "generik");

        for (int i = 0; i < bangun.Length; i++)
        {
            Console.WriteLine("objek adalah " + bangun[i].nama);
            Console.WriteLine("Luas adalah " + bangun[i].Luas());
            Console.WriteLine();
        }
    }
}

Keluaran program ditampilkan di sini:

objek adalah segitiga
Luas adalah 48

objek adalah persegipanjang
Luas adalah 100

objek adalah persegipanjang
Luas adalah 40

objek adalah segitiga
Luas adalah 24.5

objek adalah generik
Luas() harus didefinisikan-ulang
Luas adalah 0


Menggunakan Kelas Abstrak
Kadangkala Anda ingin menciptakan sebuah kelas basis yang hanya mendefinisikan format umum yang dibagikan oleh kelas-kelas terderivasinya, sehingga setiap kelas terderivasi sendiri yang menspesifikasi format detilnya. Situasi semacam ini dapat terjadi ketika kelas basis tidak bisa menciptakan implementasi berarti (bermakna) untuk sebuah metode. Inilah yang terjadi pada versi BangunDuaD yang digunakan pada program sebelumnya. Definisi atas Luas() hanya syarat belaka saja (untuk tujuan pendefinisian-ulang), yang tidak menghitung dan menampilkan luas suatu objek.

Masalah ini bisa diatasi dengan penggunaan metode abstrak, yang diciptakan dengan menspesifikasi pemodifikasi tipe abstract. Metode abstrak tidak memiliki tubuh dan, oleh karena itu, tidak diimplementasikan oleh kelas basis. Jadi, kelas terderivasi harus mendefinisikannya-ulang. Karena metode abstrak otomatis virtual, maka tidak diperlukan menggunakan pemodifikasi virtual. Untuk mendeklarasikan metode abstrak, digunakan format umum:

abstract tipe nama(daftar-param);

Seperti yang Anda lihat, tidak ada tubuh metode. Pemodifikasi abstract hanya dapat digunakan pada metode instans dan tidak dapat diterapkan pada metode static. Properti dan indekser juga bisa abstrak.

Sebuah kelas yang memuat satu atau lebih metode abstrak juga harus dideklarasikan sebagai abstrak dengan membubuhi deklarasinya dengan abstract. Karena kelas abstrak tidak mendefinisikan implementasi utuh, maka ia tidak bisa dipakai untuk menciptakan objek. Jadi, pencobaan untuk menciptakan objek dari kelas abstrak menggunakan new akan menyebabkan error kompilasi.

Ketika kelas terderivasi mewari kelas abstrak, ia harus mengimplementasikan semua metode abstrak di dalam kelas basis. Jika tidak, maka kelas terderivasi harus juga dispesifikasi sebagai abstract. Jadi, atribut abstract diwarisi sampai implementasi utuh dipenuhi.

Dengan menggunakan kelas abstrak, Anda bisa memperbaiki kelas BangunDuaD. Karena tidak ada konsep bermakna dari luas untuk bangun dua dimensi tak-terdefinisi, versi berikut mendeklarasikan Luas() sebagai abstract di dalam BangunDuaD. Hal ini secara otomatis mengakibatkan BangunDuaD sebagai kelas abstrak. Ini berarti bahwa semua kelas yang diderivasi dari BangunDuaD harus mendefinisikan-ulang Luas().

// Menciptakan kelas abstrak.
using System;

// Sebuah kelas untuk objek dua dimensi.
abstract class BangunDuaD
{
    double lebar_bangun; // sekarang private
    double tinggi_bangun; // sekarang private

    // KOnstruktor default.
    public BangunDuaD()
    {
        Lebar = Tinggi = 0.0;
        nama = "null";
    }

    // Membangun objek dengan lebar dan tinggi sama.
    public BangunDuaD(double x, string n)
    {
        Lebar = Tinggi = x;
        nama = n;
    }

    // Konstruktor terparameterisasi
    public BangunDuaD(double w, double h, string n)
    {
        Lebar = w;
        Tinggi = h;
        nama = n;
    }

    // Menciptakan sebuah salinan dari objek BangunDuaD.
    public BangunDuaD(BangunDuaD ob)
    {
      Lebar = ob.Lebar;
      Tinggi = ob.Tinggi;
      nama = ob.nama;
    }

    // Properti untuk Lebar dan Tinggi.
    public double Lebar
    {
        get { return lebar_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public double Tinggi
    {
        get { return tinggi_bangun; }
        set { tinggi_bangun = value < 0 ? -value : value; }
    }

    public string nama { get; set; }

    public void TampilDim()
    {
        Console.WriteLine("Lebar dan tinggi adalah " +
                          Lebar + " dan " + Tinggi);
    }

    // Sekarang, Luas() menjadi abstrak
    public abstract double Luas();
}

// Sebuah kelas terderivasi dari BangunDuaD untuk segitiga.
class Segitiga : BangunDuaD
{
    string Jenis; // jenis segitiga

    /* Konstruktor default, secara otomatis memanggil
       konstruktor default pada BangunDuaD. */
    public Segitiga()
    {
        Jenis = "null";
    }

    // Konstruktor untuk Segitiga.
    public Segitiga(string s, double w, double h)
        : base(w, h, "segitiga")
    {
        Jenis = s;
    }

    // Membangun segitiga sama-sisi.
    public Segitiga(double x) : base(x, "segitiga")
    {
        Jenis = "sama-sisi";
    }

    // Menciptakan sebuah salinan dari objek Segitiga.
    public Segitiga(Segitiga ob) : base(ob)
    {
        Jenis = ob.Jenis;
    }

    // Mendefinisikan-ulang Luas() untuk Segitiga.
    public override double Luas()
    {
        return Lebar * Tinggi / 2;
    }

    // Menampilkan jenis segitiga.
    public void TampilJenis()
    {
        Console.WriteLine("Segitiga adalah " + Jenis);
    }
}

// Sebuah kelas terderivasi dari BangunDuaD untuk persegipanjang.
class PersegiPanjang : BangunDuaD {
    // Konstruktor untuk PersegiPanjang.
    public PersegiPanjang(double w, double h) :
      base(w, h, "persegipanjang"){ }

    // Membangun sebuah persegi.
    public PersegiPanjang(double x) :
      base(x, "persegipanjang") { }

    // Menciptakan sebuah salinan dari objek PersegiPanjang.
    public PersegiPanjang(PersegiPanjang ob) : base(ob) { }

    // Menghasilkan true jika persegipanjang adalah persegi.
    public bool ApaPersegi()
    {
      if (Lebar == Tinggi) return true;
      return false;
    }

    // Mendefinisikan-ulang Luas() untuk PersegiPanjang.
    public override double Luas()
    {
      return Lebar * Tinggi;
    }
}

class BangunDinamis
{
    static void Main()
    {
        BangunDuaD[] bangun = new BangunDuaD[4];

        bangun[0] = new Segitiga("sama-sisi", 8.0, 12.0);
        bangun[1] = new PersegiPanjang(10);
        bangun[2] = new PersegiPanjang(10, 4);
        bangun[3] = new Segitiga(7.0);
       
        for (int i = 0; i < bangun.Length; i++)
        {
            Console.WriteLine("objek adalah " + bangun[i].nama);
            Console.WriteLine("Luas adalah " + bangun[i].Luas());
            Console.WriteLine();
        }
    }
}










No comments:

Post a Comment