Rabu, 14 Desember 2011

Antar Muka (interface), Kelas Bertingkat, dan Detail Lain



Antar Muka

Beberapa bahasa pemrograman berorientasi objek, misalnya C++, membolehkan suatu kelas memiliki dua atau lebih kelas super. Hal ini disebut pewarisan ganda (multiple inheritance). Pada ilustrasi berikut, kelas E memiliki kelas super A dan B, sedangkan kelas F memiliki 3 kelas super.



Pewarisan ganda seperti ini tidak diperbolehkan pada Java. Desainer Java ingin menjaga agar bahasa Java tetap sederhana, dan mereka merasa pewarisan ganda ini sangat kompleks dengan keuntungan yang tidak begitu besar. Akan tetapi, Java memiliki fitur lain yang bisa digunakan seperti halnya pewarisan berganda, yaitu antar muka (interface).

Kita telah mengenal istilah "antar muka" sebelumnya, yaitu dalam konteks umum tentang kotak hitam dan subrutin. Antar muka suatu subrutin terdiri dari nama, jenis keluarannya, jumlah dan tipe parameternya. Informasi ini dibutuhkan jika kita ingin memanggi subrutin tersebut. Suatu subrutin juga memiliki implementasi : yaitu blok yang berisi perintah yang akan dijalankan ketika subrutin ini dipanggil.

Dalam Java, kata interface adalah kata kunci yang memiliki arti tambahan. Suatu interface dalam hal ini adalah antar muka yang terdiri dari subrutin tanpa implementasi apa-apa. Suatu kelas dapat mengimplementasi suatu interface dengan memberikan kode detail pada setiap subrutin yang ditulis pada interface tersebut. Berikut adalah contoh interface Java sederhana :



public interface Gambar {
    public void gambar(Graphics g);
}

Deklarasi di atas mirip dengan definisi suatu kelas, akan tetapi isi metode gambar() dikosongkan. Suatu kelas yang mengimplementasi interface ini, yaitu interfaceGambar, harus mengisi implementasi metode gambar() ini. Tentunya kelas tersebut juga bisa memiliki variabel dan metode lain. Misalnya,
class Garis implements Gambar {
    public void gambar(Graphics g) {
        . . . // perintah untuk menggambar garis
    }
    . . . // variabel dan metode lain
}


Kelas apapun yang mengimplementasi antar muka Gambar[code] harus memberikan detail apa yang akan dilakukan oleh metode [code]gambar(). Objek yang diciptakan dari kelas tersebut akan memiliki metode gambar(). Perlu diingat bahwa hanya menambah metode gambar() saja tidak cukup. Definisi kelas yang ingin mengimplementasikan suatu interfaceharus menulis "implements Gambar" dalam definisi kelasnya.

Suatu kelas bisa menurunkan hanya satu kelas lain, akan tetapi suatu kelas bisa mengimplementasikan lebih dari suatu antar muka. Sebenarnya, suatu kelas bisa menurunkan kelas lain dan mengimplementasikan satu atau lebih antar muka sekaligus. Misalnya
class LingkaranBerwarna extends Lingkaran
    implements Gambar, BerisiWarna {
        . . .
    }


Intinya adalah meskipun interface bukan kelas, akan tetapi interface mirip dengan kelas. suatu interface mirip seperti kelas abstrak, yaitu kelas yang hanya digunakan untuk membuat kelas lain, bukan untuk membuat objek. Subrutin pada suatuinterface merupakan metode abstrak yang harus diimplementasikan pada kelas kongkrit yang mengimplementasikaninterface tersebut.

Seperti kelas abstrak, meskipun kita tidak bisa membuat objek dari interface, akan tetapi suatu variabel dapat bertipe suatuinterface. Misalnya, jika Gambar adalah suatu interface, dan jika Garis dan LingkaranBerwarna adalah kelas yang mengimplementasikan Gambar, maka kita bisa menulis kode seperti :
Gambar gambarku;   // Deklarasi variabel dengan tipe Gambar.
                        // Variabel ini bisa diisi objek yang
                        // mengimplementasi interface Gambar
 
gambarku = new Garis(); // gambarku berisi objek dengan kelas Garis
gambarku.gambar(g);     // memanggil metode gambar() dari kelas Garis
 
gambarku = new LingkaranBerwarna();   // Sekarang gambarku berisi objek dengan
                                      // kelas LingkaranBerwarna
gambarku.gambar(g);   // memanggil metode gambar() dari kelas LingkaranBerwarna
Variabel dengan tipe Gambar boleh merujuk pada kelas apapun yang mengimplementasikan antar muka Gambar. Pernyataan di atas seperti "gambarku.gambar(g)" boleh ditulis karena gambarku adalah variabel dengan tipe Gambar, dan setiap objek bertipe Gambar pasti memiliki metode gambar().

Catatan bahwa tipe data merupakan sesuatu yang biasa digunakan untuk mendeklarasikan variabel. Tipe data juga digunakan untuk memberikan tipe suatu parameter pada subrutin, atau sebagai tipe keluaran suatu fungsi. Pada Java, tipe data bisa berupa kelas, interface, atau salah satu dari 8 tipe data primitif. Dari semuanya, hanya kelas yang bisa digunakan untuk membuat objek baru.

Kita biasanya tidak perlu menulis interface kita sendiri hingga program kita menjadi sangat kompleks. Akan tetapi ada beberapainterface yang sudah disediakan oleh Java yang mungkin bisa digunakan atau diimplementasi dalam program kita.

Kelas Bertingkat

Suatu kelas merupakan blok bangunan suatu program, yang melambangkan suatu ide beserta data dan perilaku yang dimilikinya. Kadang-kadang kita mungkin berasa sedikit aneh untuk membuat kelas kecil hanya untuk menggabungkan beberapa data. Akan tetapi kadang-kadang kelas-kelas kecil ini sering bermanfaat dan penting. Untungnya Java membolehkan kita untuk membuat kelas di dalam kelas lain, sehingga kelas-kelas kecil ini tidak perlu berdiri sendiri. Kelas kecil ini menjadi bagian dari suatu kelas besar yang bisa melakukan hal-hal kompleks lainnya. Kelas kecil ini misalnya berguna untuk mendukung operasi yang akan dikerjakan oleh kelas besarnya.

Dalam Java, kelas bertingkat atau kelas bagian dalam adalah kelas yang ditulis di dalam definisi kelas lain. Kelas bagian dalam ini bisamemiliki nama atau anonim (tanpa nama). Kelas bagian dalam yang memiliki nama tampak seperti kelas biasa, tetapi ia ditulis di dalam kelas lain. (Kelas bagian dalam ini juga bisa memiliki kelas bagian dalam yang lain, akan tetapi ingat akan konsekuensi kerumitannya apabila kita membuat terlalu banyak tingkatan).

Seperti komponen lain dalam suatu kelas, kelas bagian dalam yang memiliki nama bisa berupa kelas statik atau kelas non-statik. Kelas bertingkat statik merupakan bagian dari struktur statik dari kelas yang menaunginya. Kelas tersebut bisa digunakan di dalam kelas induknya untuk membuat objek seperti biasa. Jika tidak dideklarasikan sebagai private, makan kelas tersebut juga bisa digunakan dari luar kelas induknya. Jika digunakan dari luar kelas induknya, namanya harus jelas mencantumkan nama kelas induknya. Mirip seperti komponen statik dari suatu kelas : kelas bertingkat statik adalah bagian kelas di mana kelas tersebut mirip dengan variabel anggota statik lainnya di dalam kelas tersebut.

Misalnya, suatu kelas bernama ModelRangkaKawat melambangkan kumpulan garis dalam ruang 3 dimensi. Misalnya kelasModelRangkaKawat memiliki kelas bertingkat statik yang bernama Garis yaitu sebuah garis. Maka dari luar kelasModelRangkaKawat, kelas Garis akan dipanggil sebagai ModelRangkaKawat.Garis.

Kelas ModelRangkaKawat dan kelas bagian dalamnya dapat dituliskan seperti berikut :
public class ModelRangkaKawat {
 
    . . . // anggota lain kelas ModelRangkaKawat
 
    static public class Garis {
        // Melambangkan garis dari titik (x1,y1,z1)
        // ke titik (x2,y2,z2) dalam ruang 3-dimensi
        double x1, y1, z1;
        double x2, y2, z2;
    } // akhir kelas Garis
 
    . . . // anggota lain kelas ModelRangkaKawat
 
} // akhir kelas ModelRangkaKawat


Di dalam kelas ModelRangkaKawat, objek Garis bisa dibuat dengan konstruktor "new Garis()". Di luar kelas, perintah "newModelRangkaKawat.Garis()" harus digunakan.

Kelas bertingkat statik memiliki akses penuh kepada anggota dari kelas induknya, termasuk ke anggota private. Mungkin ini juga motivasi sebagian orang untuk membuat kelas bertingkat, karena kelas bagian dalamnya bisa mengakses anggota private kelas lain tanpa harus membuat variabel atau metode anggotanya menjadi public.

Ketika kita mengkompilasi definisi kelas di atas, dua file kelas akan dibuat. Meskipun definisi kelas Garis berada di dalamModelRangkaKawat, akan tetapi kelas Garis akan disimpan dalam file terpisah. Nama file kelas Garis akan menjadiModelRangkaKawat$Garis.class

Kelas bertingkat yang tidak statik, pada prakteknya, tidak jauh berbeda dengan kelas bertingkat statik, akan tetapi kelas bertingkat non-statik berkaitan dengan suatu objek, bukan kelas induknya.

Anggota non-statik dari suatu kelas sebenarnya bukan merupakan bagian dari kelas itu. Hal ini juga berlaku untuk kelas bertingkat non-statik seperti juga bagian kelas non-statik lainnya. Anggota non-statik suatu kelas menjelaskan apa yang akan disimpan dalam objek yang diciptakan dari kelas tersebut. Hal ini juga berlaku (secara logis) dari kelas bertingkat non-statik.

Dengan kata lain, setiap objek yang diciptakan dari kelas induknya memiliki kopi kelas bertingkat masing-masing. Kopi ini memiliki akses ke semua variabel dan metode instansi objek tersebut. Dua objek kelas bagian dalam pada dua objek induk merupakan objek berbeda karena metode dan variabel instansi yang bisa diakses berasal dari objek yang berbeda.

Pada dasarnya, aturan untuk menentukan kapan suatu kelas bisa dimasukkan ke dalam kelas lain sebagai kelas statik atau non-statik adalah : Jika kelas tersebut perlu menggunakan variabel atau metode instansi suatu objek (bukan variabel atau metode statik kelas), maka kelas tersebut harus dibuat non-statik, jika tidak maka harus dibuat statik.

Dari luar kelas induknya, kelas bertingkat non-statik harus dipanggil dalam bentuk namaVariabel.NamaKelasBertingkat, misalnya namaVariabel adalah variabel yang merujuk pada objek yang memiliki kelas bertingkat tersebut. Sebetulnya cara ini agak langka. Kelas bertingkat non-statik biasanya digunakan hanya di dalam kelas induknya, sehingga bisa diakses dengan nama yang sederhana.

UNtuk membuat objek yang merupakan kelas bertingkat non-statik, kita harus membuat objek yang merupakan kelas induknya. (Ketika bekerja di dalam kelas, objek "this" akan secara otomatis digunakan). Objek dari kelas bertingkat tersebut dihubungkan secara permanen dengan objek dari kelas induknya, dan memiliki akses penuh atas anggota kelas induknya.

Mari lihat contoh berikut, dan mungkin bisa memberi pemahaman lebih baik bagaimana kelas bertingkat non-statik sebetulnya merupakan hal yang sangat alami. Misalnya suatu kelas yang melambangkan permainan kartu. Kelas ini memiliki kelas beringkat yang melambangkan para pemainnya. Struktur MainKartu bisa berbentuk seperti :
class MainKartu {  // Melambangkan permainan kartu
    class Pemain {  // Melambangkan salah satu pemain game ini
        .
        .
        .
    } // akhir kelas Pemain
    private Tumpukan tumpukan;      // Tumpukan kartu
 
    .
    .
    .
 
} // akhir kelas MainKartu

Jika game adalah variabel dengan tipe MainKartu, maka game memiliki kelas Pemain[code] sendiri. Dalam metode instansi objek [code]MainKartu, objek Pemain bisa dibuat dengan perintah "new Pemain()", seperti halnya kelas biasa. (Objek Pemain bisa dibuat di luar kelas MainKartu dengan perintah seperti "new game.Pemain()", tapi ini jarang dilakukan). Objek Pemain memiliki akses ke variabel instansi tumpukan dalam objek MainKartu.

Masing-masing objek MainKartu memiliki tumpukan dan Pemain sendiri-sendiri. Pemain kartu pada game tersebut akan menggunakan tumpukan kartunya sendiri sedangkan pemain kartu pada game yang lain akan menggunakan tumpukan kartu lain lagi.

Jika Pemain merupakan kelas bertingkat statik, maka pemain tersebut akan bermain di semua permainan kartu, yang tentu saja tidak mungkin terjadi.

Dalam beberapa kasus, mungkin kita harus menulis kelas bertingkat dan kemudian menggunakan kelas tersebut hanya 1 kali dalam program kita. Apakah berguna membuat kelas bertingkat jika begini kondisinya? Mungkin ya mungkin tidak. Dalam kasus seperti ini kita juga bisa membuat kelas bertingkat anonim. Kelas anonim dapat dibuat dengan menggunakan variasi dari operator new dengan bentuk
new  kelassuper_atau_interface () {
    metode_dan_variabel
}

Konstruktor ini membuat suatu kelas baru tanpa memberi nama, dan pada saat yang sama membuat objek dari kelas tersebut. Bentuk operator [code] seperti ini bisa digunakan dalam pernyataan apapun di mana pernyataan new biasa digunakan. Maksud dari pernyataan di atas adalah untuk membuat : "objek baru di dalam suatu kelas yang namanya sama dengankelassuper_atau_interface dengan ditambah dengan metode_dan_varaibel baru."

Artinya pernyataan di atas sama dengan membuat objek baru dengan konfigurasi yang baru pula. Kita juga bisa membuat kelas anonim yang diturunkan dari interface. Dalam hal ini, kelas anonim tersebut harus mengimplementasikan semua metode yang dideklarasikan oleh interface tersebut.

Kelas anonim sering digunakan untuk menangani event pada GUI (graphical user interfaces). Misalnya interface Gambar seperti didefinisikan di awal bagian ini. Misalnya kita ingin membuat objek berupa gambar bujur sangkar berisi warna merah dengan ukuran 100 x 100 piksel. Daripada membuat kelas baru kemudian menggunakan kelas tersebut untuk membuat objek, kita bisa menggunakan kelas anonim untuk membuat objek sekaligus dalam satu pernyataan :
Gambar kotakMerah = new Gambar() {
        void gambar(Graphics g) {
            g.setColor(Color.red);
            g.fillRect(10,10,100,100);
        }
};


Tanda titik koma (;) di akhir pernyataan ini bukan bagian dari definisi suatu kelas, tapi merupakan bagian dari pernyataan secara keseluruhan.

Ketika kelas Java dikompilasi, setiap kelas bertingkat anonim akan dibuat dalam file kelas terpisah. Jika nama kelas utama adalahKelasUtama, misalnya, maka nama file kelas untuk setiap kelas bertingkat anonimnya menjadi KelasUtama$1.class,KelasUtama$2.class, KelasUtama$3.class dan seterusnya.

Sifat Akses dalam Kelas

Suatu kelas dapat dideklarasikan sebagai public, yang bisa diakses dari manapun. Beberapa kelas harus dideklarasikan sebagai publik, misalnya sebagai aplikasi desktop biasa, sehingga sistem operasi bisa menjalankan prosedur main() nya. Kelas pada applet misalnya harus juga dideklarasikan sebagai public supaya bisa diakses oleh web browser.

Jika suatu kelas tidak dideklarasikan sebagai public maka ia hanya akan bisa diakses dari paket yang sama. Bagian ini membahas tentang paket. Kelas yang tidak ditulis dalam suatu paket tertentu akan dimasukkan dalam paket default.

Suatu paket seharusnya terdiri dari beberapa kelas yang saling berhubungan. Beberapa dari kelas ini memang sengaja dibuatpublic agar bisa diakses dari desktop atau program lain misalnya. Bagian lain, yang merupakan bagian internal dari bagaimana paket tersebut bekerja dan tidak boleh disentuh dari luar, tidak boleh dibuat menjadi public. Paket adalah salah satu jenis dari kotak hitam, dan kelas public dalam paket tersebut adalah antar muka dengan dunia luarnya.

Variabel atau metode anggota suatu kelas juga bisa dideklarasikan sebagai public yang juga berarti bisa diakses dari manapun. Variabel atau metode anggota ini juga bisa dideklarasikan sebagai private yang artinya hanya bisa diakses dari dalam kelas di mana dia dideklarasikan. Membuat variabel menjadi private memastikan bahwa tidak ada bagian lain yang akan bisa mengubah variabel ini kecuali dari dalam kelas atau objek itu sendiri.

Jika kita tidak memberikan sifat akses pada metode atau variabel anggota tertentu, maka ia akan otomatis bisa diakses oleh semua kelas dalam paket yang sama.

Ada satu jenis sifat akses lain yang bisa digunakan pada variabel atau metode anggota kelas, yaitu protected. Sifat protecteddigunakan apabila kita ingin variabel atau metode anggota tersebut bisa diakses oleh turunan kelas tersebut. Artinya lebih leluasa dari private tapi lebih ketat daripada public. Kelas yang didesain untuk diturunkan, biasanya memiliki anggota protected. Anggota protected digunakan untuk menambah fondasi bagi kelas turunannya, akan tetapi tetap tak terlihat dari dunia luar.

Menggabungkan Statik dan Non-Statik

Seperti disebutkan sebelumnya, kelas dapat memiliki dua kegunaan yang sangat berbeda. Kelas bisa digunakan untuk menggabungkan variabel dan subrutin statik. Atau juga bisa digunakan sebagai produsen pembuat objek. Variabel dan subrutin non-statik dalam suatu kelas akan menentukan metode dan variabel instansi pada objek yang diciptakan dari kelas tersebut. Dalam banyak kasus, suatu kelas dapat melakukan salah satu atau kedua fungsi tersebut secara bersamaan.

Dalam hal anggota statik dan non-statik digabung dalam satu kelas, kelas tersebut mengharapkan adanya interaksi antara bagian statik dan bagian non-statik dari suatu kelas. Misalnya, metode instansinya menggunakan variabel statik atau memanggil subrutin statik. Metode instansi dimiliki oleh suatu objek, bukan oleh kelas tersebut. Karena kita bisa membuat banyak objek dari suatu kelas, di mana setiap objek yang diciptakan memiliki metode instansi masing-masing. Akan tetapi akan hanya ada satu variabel statik yaitu yang dimiliki oleh suatu kelas. Dengan demikian, kita memiliki banyak objek yang bisa mengakses variabel statik tersebut bersama-sama.

Misalnya anggap kita akan menulis kelas PasanganDadu yang menggunakan kelas Random seperti pada bagian sebelumnya untuk mengocok dadu. Objek PasanganDadu perlu mengakses objek Random. Akan tetapi membuat objek Random untuk setiap objekPasanganDadu adalah terlalu berlebihan, karena fungsinya hanya digunakan untuk menghasilkan nilai acak saja. Solusi yang bagus adalah dengan menggunakan variabel static yang digunakan oleh semua objek yang dibuat dari kelas PasanganDadu. Misalnya pada kode berikut ini :
class PasanganDadu {
    private static Random randGen = new Random();
    // (Catatan:  java.util.Random telah diimpor sebelum kelas ini dibuat)
 
    public int dadu1;   // Angka pada dadu pertama
    public int dadu2;   // Angka pada dadu kedua
 
    public PasanganDadu() {
        // Konstruktor. Membuat pasangan dadu dengan angka
        // awal berupa bilangan acak
        kocok();
    }
 
    public void kocok() {
        // Kocok dadu dengan membuat masing-masing dadu
        // bernilai bilangan acak 1 hingga 6
        dadu1= randGen.nextInt(6) + 1;
        dadu2= randGen.nextInt(6) + 1;
    }
 
} // akhir kelas PasanganDadu
Contoh lain adalah kelas Murid yang digunakan pada bagian sebelumnya. Kita tambahkan variabel instansi nomorMurid yaitu nomor unik yang berbeda untuk setiap murid. Untuk itu kita perlu melacak nomor baru yang belum dipakai dengan variabelnomorBerikutnya yang berbentuk variabel statik sehingga semua objek akan mengacu pada variabel yang sama. Ketika objek baru dibuat, objek baru akan mengambil nilai nomorBerikutnya untuk dijadikan nomorMurid yang baru.
public class Murid {
 
    private String nama;  // Nama murid
    private int nomorMurid;  // nomor murid unik
    public double ujian1, ujian2, ujian3;   // Nilai ujian
 
    private static int nomorBerikutnya = 0;
    // simpan nomor murid berikutnya
 
    Murid(String namaBaru) {
        // Konstruktor objek Murid:
        // memberi nama, dan memberi nomor murid baru
        nama = namaBaru;
        nomorBerikutnya++;
        nomorMurid = nomorBerikutnya;
    }
 
    public String getNama() {
        // Fungsi untuk mengambil isi variabel instansi private: nama
        return nama;
    }
 
    public int getNomorMurid() {
        // Fungsi untuk membaca isi nomorMurid
        return nomorMurid;
    }
 
    public double hitungRataRata() {
        // Hitung rata-rata nilai ujian
        return (ujian1 + ujian2 + ujian3) / 3;
    }
 
}  // akhir kelas Murid


Inisialisasi "nomorBerikutnya = 0" hanya dilakukan satu kali, yaitu ketika kelas ini pertama kali dipanggil (pada saat program dijalankan). Ketika objek baru bertipe Murid dibuat, dan di dalam konstruktor perintah "nomorBerikutnya++;", maka nomor berikutnya akan disimpan untuk digunakan pada objek baru lainnya.

Ketika objek pertama dibuat, nilai nomorBerikutnya akan bernilai 1. Ketika objek kedua dibuat, nilai nomorBerikutnya bernilai 2, dan seterusnya. Konstruktor akan menyimpan nilai baru nomorBerikutnya pada variabel instansinya sendiri yang tidak di-share dengan objek-objek lain yaitu nomorMurid. Dengan cara ini setiap murid baru akan selalu memiliki nomorMurid baru yang berbeda satu dengan yang lain.

Tidak ada komentar:

Posting Komentar