Tuesday, March 27, 2018

7. Kelas Java Tingkat Lanjut




Pada Bab 4, Anda telah mempelajari dasar-dasar pendefinisian kelas sendiri yang disertai dengan sejumlah contoh ilustratif. Setelah melihat beberapa program contoh pada Bab 5 dan Bab 6, Anda sekarang telah siap untuk mengkaji topik-topik lanjut dalam mendefinisikan kelas sendiri. Selain mengenalkan sejumlah topik baru, Anda akan mengkaji-ulang beberapa topik dari Bab 4 dan mendiskusikannya secara lebih mendalam. Pada Bab 5 dan Bab 6, Anda telah mempelajari kelas Pecahan untuk mengenal beberapa konsep baru. Anda akan tetap mempelajari kelas Pecahan ini untuk mengenal sejumlah konsep kunci di bab ini. Sampai akhir bab ini, Anda akan terus mengembangkan dan menyempurnakan definisi dari kelas Pecahan. Selain kelas Pecahan, Anda juga akan melihat sejumlah kelas lain untuk membantu Anda dalam memahami konsep-konsep kunci pada bab ini.


7.1 Menghasilkan Objek Sebagai Nilai Balik Dari Metode
Sampai titik ini, ketika Anda mendefinisikan sebuah metode penghasil-nilai-balik, nilai balik yang dihasilkan oleh metode tersebut bertipe data primitif, seperti int, boolean, atau String. Pada subbab ini, Anda akan belajar bagaimana menghasilkan objek sebagai nilai balik dari metode. Ingat bahwa String adalah sebuah objek, jadi, Anda sebenarnya telah mengetahui bagaimana menghasilkan objek dari sebuah metode. Namun, String umumnya diperlakukan sebagai tipe data primitif. Di sini, Anda akan mempelajari gambaran utuh tentang apa yang sebenarnya terjadi ketika sebuah metode menghasilkan nilai balik berupa objek dan bagaimana mendefinisikan kelas secara kompleks.

Anda akan kembali mempelajari kelas Pecahan untuk membantu memahami bagaimana objek dihasilkan oleh sebuah metode. Berikut merupakan bagian dari definisi kelas yang mencakup konstruktor, aksesor, dan mutator (Anda akan menambahkan metode-metode lain secara bertahan seiring dengan banyaknya topik yang dirangkum):

// Mendemonstrasikan kelas Pecahan
class Pecahan {
   private int numerator;
   private int denominator;

   // Konstruktor dua-parameter
   public Pecahan(int num, int denom) {
      setNumerator(num);
      setDenominator(denom);
   }
 
   // Aksesor
   public int getDenominator( ) {
      return denominator;
   }
 
   // Aksesor
   public int getNumerator( ) {
      return numerator;
   }
 
   // Mutator
   public void setDenominator(int denom) {
      if (denom == 0) {
         //Error fatal
         System.err.println("Error Fatal");
         System.exit(1);
      }
  
      denominator = denom;
   }
  
   // Mutator
   public void setNumerator(int num) {
      numerator = num;
   }
  
   public String toString( ) {
      return getNumerator() + "/" + getDenominator();
   }
 
   // Metode-metode lain di sini
}

Perhatikan bahwa Anda tidak mengijinkan adanya sebuah pecahan untuk memiliki 0 sebagai denominatornya (penyebutnya). Jika didapati denominator bernilai 0, maka program akan dihentikan, menggunakan System.exit(1). Anda akan mempelajari penanganan eksepsi seperti ini pada Bab 8.

Sekarang, Anda akan menuliskan metode dengan nama sederhanakan() yang mereduksi sebuah pecahan menjadi bentuk paling sederhananya. Bagaimana menurut Anda kode berikut?

public void sederhanakan() {
   int num = getNumerator();
   int denom = getDenominator();
  
   int gcd = gcd(num, denom);
   
   setNumerator(num/gcd);
   setDenominator(denom/gcd);
}

Anda menggunakan metode gcd yang menghasilkan pembagi bersama terbesar (int) seperti yang telah dijelaskan pada Bab 6. Anda mendapatkan bentuk sederhana dengan cara membagi numerator dan denominator dengan nilai gcdnya. Berikut adalah contoh penggunaan metode tersebut:

//Menguji kelas Pecahan
public class UjiPecahan {
 public static void main(String args[]) {
  Pecahan f1 = new Pecahan(24, 36);
  System.out.println("Sebelum penyederhanaan: " + f1);
  
  f1.sederhanakan(); //f1 disederhanakan!
  System.out.println("Setelah penyederhanaan: " + f1);
 }
}

Jika program tersebut dijalankan, dihasilkan keluaran berikut:

Sebelum penyederhanaan: 24/36
Setelah penyederhanaan: 2/3

Perhatikan bahwa nilai dari f1 sekarang telah berubah karena metode sederhanakan() memperbarui anggota-anggota penerima objek.

Gambar 7.1 Ilustrasi ini menunjukkan keadaan memori setelah objek sederhana diciptakan dan ditugasi nilai-nilai


Apakah hal itu bisa diterima bila Anda mengubah nilai-nilai dari objek penerima f1? Pada kasus ini, tidak. Akan lebih baik jika mempertahankan nilai-nilai dari objek penerima f1 dan menghasilkan sebuah objek Pecahan yang baru yang memiliki bentuk pecahan yang sederhana. Ini akan memberikan fleksibilitas pada programer klien (pengguna kelas).

Berikut adalah versi terperbaiki dari metode sederhanakan():

public Pecahan sederhanakan() {
   int num = getNumerator();
   int denom = getDenominator();
     
   int gcd = gcd(num, denom);
     
   Pecahan sederhana = new Pecahan(num/gcd, denom/gcd);
   return sederhana;
}

Berikut adalah kode untuk menguji metode sederhana() di atas:

//Menguji kelas Pecahan
public class UjiPecahan {
   public static void main(String args[]) {
      Pecahan f1, f2;
      f1 = new Pecahan(24, 36);
      f2 = f1.sederhanakan();
  
      System.out.println(f1.toString() + " dapat direduksi menjadi " +
  f2.toString());
 }
}

Jika program tersebut dijalankan akan dihasilkan keluaran yang sama seperti versi program sebelumnya.

Anda tentu sekarang mengetahui bahwa nilai-nilai pada objek f1 sekarang tidak berubah. Sekarang, jika Anda ingin mereduksi f1 menjadi bentuk pecahan sederhananya, maka Anda hanya perlu menugaskan hasil kembali kepada objek f1 seperti berikut:

f1 = f1.sederhanakan();

Sekarang, Anda akan melihat efek dari metode sederhana() ketika ia menghasilkan nilai balik berupa sebuah objek. Gambar 7.1 menunjukkan keadaan memori setelah objek sederhana diciptakan dan ditugasi nilai-nilai.

Gambar 7.2 Kelanjutan dari Gambar 7.1. Ilustrasi ini menunjukkan bahwa sebuah objek (sebenarnya referensi yang menunjuk objek itu) dikembalikan kepada program pemanggil.


Perhatikan bahwa sederhana merupakan sebuah variabel lokal, tetapi objek itu sendiri diciptakan pada memori heap. Statemen return di akhir dari metode sederhana() menghasilkan nilai dari sederhana, yang merupakan sebuah referensi ke objek Pecahan. Gambar 7.2 menunjukkan keadaan memori setelah metode sederhana() selesai dieksekusi. Nilai dari sederhana sekarang ditugaskan kepada objek f2, dan sekarang variabel f2 menunjuk ke objek Pecahan ini. Penting disadari bahwa ketika dikatakan menghasilkan objek dari sebuah metode, sebenarnya yang terjadi adalah menghasilkan sebuah referensi ke suatu objek.


7.2 Menggunakan Katakunci this
Anda akan terus mengembangkan implementasi dari kelas Pecahan. Sekarang perhatikan empat operasi aritmatika untuk pecahan pada Gambar 7.3. Ketika mendefinisikan metode-metode untuk keempat operasi aritmatika tersebut, Anda akan menggunakan katakunci this. Katakunci ini dikenal juga sebagai pointer referensoi-diri, karena ia dipakai untuk mereferensi objek penerima di dalam metode objek.

Gambar 7.3 Aturan pecahan untuk penjumlahan, perkalian, pengurangan, dan pembagian.


Anda akan memulainya dengan metode jumlah() yang menjumlahkan dua pecahan:

   public Pecahan jumlah(Pecahan pecah) {
      int a, b, c, d;
      Pecahan jum;
    
      a = this.getNumerator();   //membaca num dan den dari
      b = this.getDenominator(); //objek penerima
    
      c = pecah.getNumerator();   //membaca num dan den dari
      d = pecah.getDenominator(); //objek pecah
      
      jum = new Pecahan(a*d + b*c, b*d);
    
      return jum;
   }

Pertama, perhatikan bagaimana metode jumlah() ini digunakan. Kode berikut menjumlahkan dua pecahan f1 dan f2 dan menugaskan hasil penjumlahannya kepada f3:

//Menguji kelas Pecahan untuk penjumlahan pecahan
public class UjiPecahan2 {
   public static void main(String args[]) {
      Pecahan f1, f2, f3;
    
      f1 = new Pecahan(1, 2);
      f2 = new Pecahan(1, 4);
      f3 = f1.jumlah(f2);
   
      System.out.println("Penjumlahan atas " + f1.toString() + " dan " +
    f2.toString() + " adalah " + f3.toString());
 }
}

Jika program tersebut dijalankan, dihasilkan keluaran berikut:

Penjumlahan atas 1/2 dan 1/4 adalah 6/8

Pada statemen:

f3 = f1.jumlah(f2);

Anda memanggil metode jumlah() dari objek f1 dan dengan melewatkan argumen objek f2. Jadi pada kasus ini, objek penerima adalah f1. Gambar 7.4 menunjukkan keadaan memori pada titik dimana metode jumlah() dari objek f1 dipanggil. Perhatikan bahwa pointer referensi-diri this mereferensi objek f1 karena ia adalah objek penerima.

Gambar 7.4 Ilustrasi ini menunjukkan keadaan memori untuk f1.jumlah(f2).


Karena f2 juga sebuah objek Pecahan, Anda bisa menuliskan statemen seperti:

f3 = f2.jumlah(f1);

dan mendapatkan hasil yang sama (karena ini merupakan operasi penjumlahan). Pada kasus ini, objek penerima adalah f2, dan argumen yang dilewatkan adalah f1. Gambar 7.5 menunjukkan keadaan memori pada titik dimana metode jumlah() dari objek f2 dipanggil. Perhatikan bahwa objek-objek yang direferensi oleh pecah dan this ditukar. Kali ini pointer referensi-diri this menunjuk ke objek f2.

Metode jumlah() menghitung penjumlahan atas objek penerima Pecahan dan objek argumen Pecahan. Anda menggunakan pecah untuk parameter, sehingga

c = pecah.getNumerator();   //membaca num dan den dari
d = pecah.getDenominator(); //objek pecah

akan membaca numerator dan denumerator dari objek argumen Pecahan.

Gambar 7.5 Ilustrasi ini menunjukkan keadaan memori untuk f2.jumlah(f1).

Untuk membaca numerator dan denominator dari objek Pecahan penerima, Anda menuliskan:

a = this.getNumerator();   //membaca num dan den dari
b = this.getDenominator(); //objek penerima

karena katakunci this menunjuk ke objek penerima.


Selanjutnya  >>>


No comments: