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