Interface (Antar Muka)
// 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
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 interface
Gambar, 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 interface
harus 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 suatu interface
merupakan metode abstrak yang harus diimplementasikan pada kelas kongkrit yang mengimplementasikan interface tersebut
.Seperti kelas abstrak, meskipun kita tidak bisa membuat objek dari
Gambar gambarku; // Deklarasi variabel dengan tipe Gambar.interface
, akan tetapi suatu variabel dapat bertipe suatu interface
. Misalnya, jika Gambar
adalah suatu interface
, dan jika Garis
dan LingkaranBerwarna
adalah kelas yang mengimplementasikan Gambar
, maka kita bisa menulis kode seperti, // 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 beberapa interface
yang sudah disediakan oleh Java yang mungkin bisa digunakan atau diimplementasi dalam program kita.Class 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 bisa memiliki 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 kelas ModelRangkaKawat
memiliki kelas bertingkat statik yang bernama Garis
yaitu sebuah garis. Maka dari luar kelas ModelRangkaKawat
, 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 "new ModelRangkaKawat.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 dalam ModelRangkaKawat
, akan tetapi kelas Garis
akan disimpan dalam file terpisah. Nama file kelas Garis
akan menjadi ModelRangkaKawat$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 dengan kelassuper_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 adalah
KelasUtama
, 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 Class
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 dibuat
public
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 protected
digunakan 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 objek PasanganDadu
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 variabel nomorBerikutnya
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.