Monday, December 19, 2016

Bab 1. Java Struktur Data dan Pemrograman GUI



Bab.1 Kelas Abstrak dan Antarmuka


1.1 Introduksi

Anda mungkin telah belajar membuat program-program sederhana untuk menciptakan dan menampilkan komponen-komponen GUI. Sudahkah Anda menulis kode untuk merespon aksi atau tindakan pengguna, seperti pengklikan suatu tombol? Seperti yang ditampilkan pada Gambar 1.1, ketika suatu tombol diklik, pesan ditampilkan pada konsol.

Gambar 1.1 Program merespon kejadian aksi pengklikan-tombol


Untuk menulis kode seperti itu, Anda perlu mengetahui tentang antarmuka. Suatu antarmuka digunakan untuk mendefinisikan watak umum kelas-kelas (khususnya yang saling tidak berelasi). Sebelum mendiskusikan antarmuka, akan didiskusikan topik yang sangat berkorelasi dengan antarmuka, kelas abstrak.


1.2 Kelas Abstrak
Pada Bab.11, dalam buku JAVA: Teori dan Implementasi, ObjekGeometri didefinisikan sebagai superkelas untuk Lingkaran dan PersegiPanjang.ObjekGeometri memodelkan fitur-fitur umum objek geometri. Kedua kelas Lingkaran dan PersegiPanjang memuat metode dapatLuas() dan metode dapatKeliling() untuk menghitung luas dan keliling suatu lingkaran.Karena Anda dapat menghitung luas dan keliling semua objek geometri, maka lebih baik mendefinisikan dapatLuas() dan dapatKeliling() dalam kelas ObjekGeometri. Namun, kedua metode tersebut tidak bisa diimplementasikan di dalam kelas ObjekGeometri, karena implementasinya bergantung pada tipe spesifik suatu objek geometri. Metode seperti itu dikenal dengan metode abstrak dan ditandai dengan penggunaan pemodifikasi abstract pada header metode.Setelah Anda mendefinisikan kedua metode tersebut di dalam ObjekGeometri, maka ObjekGeometri menjadi kelas abstrak. Kelas abstrak ditandai dengan penggunaan pemodifikasi abstract pada header kelas. Dalam notasi diagram UML, nama kelas abstrak dan metode abstrak dibuat huruf miring, seperti ditampilkan pada Gambar 1.2. Kode1.1 menyajikan kode sumber untuk kelas ObjekGeometri.

Kode1.1 ObjekGeometri.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public abstract class ObjekGeometri {
  private String warna = "putih";
  private boolean terisi;
  private java.util.Date tanggalDiciptakan;

  /** Menciptakan suatu objek geometri default */
  protected ObjekGeometri() {
    tanggalDiciptakan = new java.util.Date();
  }

  /** Menciptakan suatu objek geometri dengan warna dan nilai terisi tertentu
  protected ObjekGeometri(String warna, boolean terisi) {
    tanggalDiciptakan = new java.util.Date();
    this.warna = warna;
    this.terisi = terisi;
  }

  /** Mengembalikan warna */
  public String dapatWarna() {
    return warna;
  }

  /** Menetapkan suatu warna baru */
  public void dapatWarna(String warna) {
    this.warna = warna;
  }

  /** Mengembalikan terisi. Karena terisi adalah suatu boolean,
    metode dapat dinamai apaTerisi */
  public boolean apaTerisi() {
    return terisi;
  }

  /** Menetapkan suatu nilai terisi yang baru */
  public void tetapkanTerisi(boolean terisi) {
    this.terisi = terisi;
  }

  /** Mendapatkan tanggalDiciptakan */
  public java.util.Date dapatTanggalDiciptakan() {
    return tanggalDiciptakan;
  }

  /** Mengembalikan suatu representasi string atas objek ini */
  public String keString() {
    return "diciptakan pada " + tanggalDiciptakan + "\nwarna: " + warna +
   " dan nilai terisi: " + terisi;
  }

  /** Metode abstrak dapatLuas */
  public abstract double dapatLuas();

  /** Metode abstrak dapatKeliling */
  public abstract double dapatKeliling();
 }

Kelas abstrak sama dengan kelas biasa, tetapi Anda tidak bisa menciptakan instans dari kelas abstrak menggunakan operator new.Metode abstrak didefinisikan tanpa implementasi. Implementasinya disediakan oleh subkelas.Suatu kelas yang memuat metode abstrak harus didefinisikan abstrak.

Konstruktor dalam kelas abstrak harus didefinisikan terproteksi, karena hanya digunakan oleh subkelasnya saja. Ketika Anda menciptakan suatu instans dari suatu subkelas konkrit, konstruktor superkelasnya dipanggil untuk menginisialisasi bidang data yang didefinisikan di dalam superkelas.

Kelas abstrak ObjekGeometri mendefinisikan fitur-fitur umum (data dan metode) untuk objek-objek geometri dan menyediakan konstruktor-konstruktor yang sesuai. Karena Anda tidak mengetahui bagaimana menghitung luas dan keliling suatu objek geometri, maka dapatLuas dan dapatKeliling didefinisikan sebagai metode-metode abstrak. Kedua metode ini diimplementasikan di dalam sub-sub kelas.

Gambar1.2 Kelas ObjekGeometri memuat metode-metode abstrak



Kode1.2 Lingkaran.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Lingkaran extends ObjekGeometri{
  private double radius;

  public Lingkaran() {
  }

  public Lingkaran (double radius) {
    this.radius = radius;
  }

  public Lingkaran(double radius, String warna, boolean terisi) {
    this.radius = radius;
    dapatWarna(warna);
    tetapkanTerisi(terisi);
  }

  /** Mengembalikan radius */
  public double dapatRadius() {
    return radius;
  }

  /** Menetapkan suatu radius baru */
  public void tetapkanRadius(double radius) {
    this.radius = radius;
  }

  /** Mengembalikan luas */
  public double dapatLuas() {
    return radius * radius * Math.PI;
  }

  /** Mengembalikan diameter */
  public double dapatDiameter() {
    return 2 * radius;
  }

  /** Mengembalikan keliling */
  public double dapatKeliling() {
    return 2 * radius * Math.PI;
  }

  /* Menampilkan informasi lingkaran */
  public void tampilLingkaran() {
  System.out.println("Lingkaran diciptakan pada "+ dapatTanggalDiciptakan() 
   + " dan radiusnya adalah " + radius);
  }
}

Kode1.3 PersegiPanjang.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class PersegiPanjang extends ObjekGeometri {
  private double lebar;
  private double tinggi;

  public PersegiPanjang() {
  }

  public PersegiPanjang(double lebar, double tinggi) {
    this.lebar = lebar;
    this.tinggi = tinggi;
  }

  public PersegiPanjang(double lebar, double tinggi, String warna,
      boolean terisi) {
    this.lebar = lebar;
    this.tinggi = tinggi;
    dapatWarna(warna);
    tetapkanTerisi(terisi);
  }

  /** Mengembalikan lebar */
  public double dapatLebar() {
    return lebar;
  }

  /** Menetapkan suatu lebar baru */
  public void dapatLebar(double lebar) {
    this.lebar = lebar;
  }

  /** Mengembalikan tinggi */
  public double dapatTinggi() {
    return tinggi;
  }

  /** Menetapkan suatu tinggi baru */
  public void tetapkanTinggi(double tinggi) {
    this.tinggi = tinggi;
  }

  /** Mengembalikan luas */
  public double dapatLuas() {
    return lebar * tinggi;
  }

  /** Mengembalikan keliling persegi-panjang */
  public double dapatKeliling() {
    return 2 * (lebar + tinggi);
  }
}



1.2.1 Apa Keuntungan Metode Abstrak?
Anda mungkin bertanya-tanya apakah keuntungan yang didapatkan dari penggunaan dapatLuas dan dapatKeliling sebagai metode abstrak dalam kelas ObjekGeometri bila dibandingkan bila hanya mendefinisikan kedua metode tersebut di dalam tiap subkelas. Contoh berikut ini menunjukkan keuntungan pendefinisian kedua metode tersebut di dalam kelas ObjekGeometri.

Kode1.4 menciptakan dua objek geometri, suatu lingkaran dan suatu persegi-panjang, memanggil metode luasSama untuk memeriksa apakah keduanya memiliki luas yang sama dan memanggil metode tampilObjekGeometri untuk menampilkannya.

Kode1.4 UjiObjekGeometri.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class UjiObjekGeometri {
  /** Main method */
  public static void main(String[] args) {
    // Menciptakan dua objek geometri
    ObjekGeometri geoObjek1 = new Lingkaran(5);
    ObjekGeometri geoObjek2 = new PersegiPanjang(5, 3);

    System.out.println("Dua objek memiliki luas sama? " +
      luasSama(geoObjek1, geoObjek2));

    // Menampilkan lingkaran
    tampilObjekGeometri(geoObjek1);

    // Menampilkan persegi-panjang
    tampilObjekGeometri(geoObjek2);
  }

  /** Suatu metode untuk membandingkan luas dua objek geometri */
  public static boolean luasSama(ObjekGeometri geoObjek1,
   ObjekGeometri geoObjek2){
    return geoObjek1.dapatLuas() == geoObjek2.dapatLuas();
  }

  /** Suatu metode untuk menampilkan suatu objek geometri */
  public static void tampilObjekGeometri(ObjekGeometri objek){
    System.out.println();
    System.out.println("Luas adalah " + objek.dapatLuas());
    System.out.println("Keliling adalah " + objek.dapatKeliling());
  }
}

Keluaran
Dua objek memiliki luas sama? false

Luas adalah 78.53981633974483
Keliling adalah 31.41592653589793

Luas adalah 15.0
Keliling adalah 16.0

Metode dapatLuas dan dapatKeliling didefinisikan dalam kelas ObjekGeometri dioverride dalam kelas Lingkaran dan kelas PersegiPanjang. Statemen 5-6

ObjekGeometri geoObjek1 = new Lingkaran(5);
ObjekGeometri geoObjek2 = new PersegiPanjang(5, 3);

menciptakan suatu lingkaran dan suatu persegi-panjang dan menugaskan keduanya kepada variabel geoObjek1 dan geoObjek2.Kedua variabel tersebut bertipe ObjekGeometri.

Ketika memanggil luasSama(geoObjek1, geoObjek2) (pada baris 9), metode dapatLuas yang didefinisikan dalam kelas Lingkaran digunakan untuk geoObjek1.dapatLuas(), karena geoObjek1 adalah suatu lingkaran dan metode dapatLuas yang didefinisikan dalam kelas PersegiPanjang digunakan untuk geoObjek2.dapatLuas(), karena geoObjek2 adalah suatu persegi-panjang.

Dengan logika yang sama, ketika memanggil tampilObjekGeometri(geoObjek1) (pada baris 12), metode dapatLuas dan dapatKeliling yang didefinisikan dalam kelas Lingkaran digunakan dan ketika memanggil tampilObjekGeometri(geoObjek2) (pada baris 15), metode dapatLuas dan dapatKeliling yang didefinisikan dalam kelas PersegiPanjang digunakan.


1.2.2 Hal-Hal Penting Pada Kelas Abstrak
Berikut adalah hal-hal penting seputar kelas abstrak:
·         Metode abstrak tidak dapat dimuat dalam suatu kelas nonabstrak. Jika subkelas dari suatu superkelas abstrak tidak mengimplementasikan semua metode abstrak, maka subkelas tersebut harus didefinisikan abstrak. Dengan kata lain, di dalam suatu subkelas nonabstrak yang mewarisi dari superkelas abstrak, semua metode abstrak harus diimplementasikan. Perhatikan bahwa metode abstrak bersifat nonstatik.
·         Kelas abstrak tidak bisa diinstansiasi menggunakan operator new, tetapi Anda masih bisa mendefinisikan konstruktor-konstruktornya, yang dipanggil dari konstruktor-konstruktor subkelas. Sebagai contoh, konstruktor ObjekGeometri dapat dipanggil dari kelas Lingkaran dan dari kelas PersegiPanjang.
·         Suatu kelas yang memuat metode abstrak harus didefinisikan abstrak. Akan tetapi, adalah hal mungkin untuk mendefinisikan kelas abstrak yang tak memuat satupun metode abstrak. Pada kasus ini, Anda tidak bisa menciptakan instans kelas menggunakan operator new. Kelas ini digunakan sebagai kelas basis untuk mendefinisikan sub-subkelas baru.
·         Suatu kelas dapat menjadi abstrak bahkan jika superkelasnya konkrit. Sebagai contoh, kelas Object adalah konkrit, tetapi sub-subkelasnya dapat konrit, seperti ObjekGeometri.
·         Suatu subkelas dapat mengoverride suatu metode dari superkelas yang mendefinisikannya sebagai abstrak. Hal ini sangat tidak biasa tetapi penting ketika implementasi metode di dalam superkelas menjadi invalid di dalam subkelas. Pada kasus ini, subkelas tersebut dapat didefinisikan sebagai abstrak.
·         Anda tidak bisa menciptakan suatu instans dari kelas abstrak menggunakan operator new, tetapi suatu kelas abstrak dapat digunakan sebagai tipe data. Oleh karena itu, statemen berikut ini, yang menciptakan suatu array yang memiliki elemen-elemen bertipe ObjekGeometri, adalah benar:

ObjekGeometri[] objek = new ObjekGeometri [10];

Anda kemudian dapat menciptakan suatu instans ObjekGeometri dan menugaskan referensinya kepada array seperti ini:

objek[0] = new Lingkaran();

1.3 Contoh: Calendar dan GregorianCalendar
Suatu instans java.util.Date merepresentasikan waktu spesifik dalam kepresisian milidetik. java.util.Calendar merupakan suatu kelas basis untuk mengekstrak informasi kalender yang detil, seperti tahun, bulan, tanggal, jam, menit, dan detik. Sub-subkelas dari Calendar dapat mengimplementasikan sistem-sistem kalender spesifik, seperti kalender Gregorian, kalender Lunar, dan kalender Yahudi. Saat ini, java.util.GregorianCalendar untuk kalender Gregorian telah didukung oleh JAVA, seperti ditampilkan pada Gambar 1.3. Metode add adalah metode abstrak dalam kelas Calendar, karena implementasinya tergantung pada sistem kalender konkrit.


Gambar 1.3 Kelas Calendar abstrak mendefinisikan fitur-fitur umum berbagai sistem kelender.


Anda dapat menggunakan new GregorianCalendar() untuk mengkonstruksi GregorianCalendar default dengan waktu sekarang dan new GregorianCalendar(year, month, date) untuk mengkonstruksi GregorianCalendar dengan tahun, bulan, dan tanggal tertentu.Parameter month dimulai dari 0, dimana 0 adalah Januari.

Metode get(int field) didefinisikan di dalam kelas Calendar berguna untuk mengekstrak informasi tanggal dan waktu dari suatu objek Calendar. Beberapa field didefinisikan sebagai konstanta, seperti tertampil pada Tabel 1.1.

Tabel 1.1 Konstanta-konstanta field di dalam kelas Calendar
Konstanta
Deskripsi
YEAR
MONTH
DATE
HOUR
HOUR_OF_DAY
MINUTE
SECOND
DAY_OF_WEEK

DAY_OF_MONTH
DAY_OF_YEAR

WEEK_OF_MONTH

WEEK_OF_YEAR

AM_PM
Tahun kalender.
Bulan kalender dengan 0 untuk Januari.
Hari kalender.
Jam kelender (notasi 12-jam).
Jam kelender (notasi 24-jam).
Menit kalender.
Detik Kalender.
Jumlah hari dalam seminggu dengan 1 untuk Minggu.
Sama dengan DATE.
Jumlah hari dalam setahun dengan 1 untuk hari pertama.
Jumlah minggu dalam sebulan dengan 1 untuk minggu pertama.
Jumlah minggu dalam setahun dengan 1 untuk minggu pertama.
Indikator untuk AM atau PM (0 untuk AM dan 1 untuk PM).

Kode1.5 UjiKalender.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.*;

public class UjiKalender {
  public static void main(String[] args) {
    // Menciptakan suatu kalender Gregorian untuk tanggal dan waktu saat ini
    Calendar kalender = new GregorianCalendar();
    System.out.println("Waktu sekarang adalah " + new Date());
    System.out.println("YEAR:\t" + kalender.get(Calendar.YEAR));
    System.out.println("MONTH:\t" + kalender.get(Calendar.MONTH));
    System.out.println("DATE:\t" + kalender.get(Calendar.DATE));
    System.out.println("HOUR:\t" + kalender.get(Calendar.HOUR));
    System.out.println("HOUR_OF_DAY:\t" +
     kalender.get(Calendar.HOUR_OF_DAY));
    System.out.println("MINUTE:\t" + kalender.get(Calendar.MINUTE));
    System.out.println("SECOND:\t" + kalender.get(Calendar.SECOND));
    System.out.println("DAY_OF_WEEK:\t" +
     kalender.get(Calendar.DAY_OF_WEEK));
    System.out.println("DAY_OF_MONTH:\t" +
     kalender.get(Calendar.DAY_OF_MONTH));
    System.out.println("DAY_OF_YEAR: " +
     kalender.get(Calendar.DAY_OF_YEAR));
    System.out.println("WEEK_OF_MONTH: " +
     kalender.get(Calendar.WEEK_OF_MONTH));
    System.out.println("WEEK_OF_YEAR: " +
     kalender.get(Calendar.WEEK_OF_YEAR));
    System.out.println("AM_PM: " + kalender.get(Calendar.AM_PM));

    // Menciptakan kalender untuk 11 September 2001
    Calendar kalender1= new GregorianCalendar(2001, 8, 11);
    System.out.println("September 11, 2001 adalah hari " +
     namaHariDalamMinggu(kalender1.get(Calendar.DAY_OF_WEEK)));
  }

  public static String namaHariDalamMinggu(int hariDalamMinggu) {
    switch (hariDalamMinggu) {
      case 1: return "Minggu";
      case 2: return "Senin";
      case 3: return "Selasa";
      case 4: return "Rabu";
      case 5: return "Kamis";
      case 6: return "Jumat";
      case 7: return "Sabtu";
      default: return null;
    }
  }
}

Keluaran
Waktu sekarang adalah Wed Nov 21 16:47:18 ICT 2012
YEAR:   2012
MONTH:  10
DATE:   21
HOUR:   4
HOUR_OF_DAY:    16
MINUTE: 47
SECOND: 18
DAY_OF_WEEK:    4
DAY_OF_MONTH:   21
DAY_OF_YEAR: 326
WEEK_OF_MONTH: 4
WEEK_OF_YEAR: 47
AM_PM: 1
September 11, 2001 adalah hari Selasa

Metode set(int field, value) yang didefinisikan di dalam kelas Calendar dapat digunakan untuk menetapkan field. Sebagai contoh, Anda dapat menggunakan kalender.set(Calendar.DAY_OF_MONTH, 1) untuk menetapkan kalender menjadi hari pertama dalam sebulan.

Metode add(field, value) menambahkan jumlah waktu tertentu kepada field yang diberikan. Sebagai contoh, add(Calendar.DAY_OF_MONTH, 5) menambahkan lima hari kepada waktu sekarang pada kalender dan add(Calendar.DAY_OF_MONTH, -5) mengurangkan lima hari dari waktu sekarang pada kalender.
Untuk mendapatkan jumlah hari dalam satu bulan, digunakan kalender.getActualMaximum(Calendar.DAY_OF_MONTH). Sebagai contoh, jika kalender adalah Maret, maka metode akan mengembalikan nilai 31.

Anda bisa menetapkan suatu waktu di dalam objek Date pada kalender dengan memanggil kalender.setTime(date) dan mengekstrak waktu tersebut dengan memanggil kalender.getTime().

1.4 Antarmuka
Antarmuka adalah konstruksi seperti kelas yang hanya memuat konstanta-konstanta dan metode-metode abstrak. Dalam berbagai kasus, antarmuka sama dengan kelas abstrak, tetapi tujuannya adalah untuk menentukan watak umum dari objek. Sebagai contoh, dengan menggunakan antarmuka yang sesuai, Anda dapat menentukan apakah antarmuka komparabel (comparable), edibel (edible), atau klonabel (cloneable).

Untuk membedakan antarmuka dari kelas, JAVA menggunakan sintaks berikut ini untuk mendefinisikan suatu antarmuka:

pemodifikasi interface namaAntarMuka {
  /** Deklarasi konstanta-konstanta */
  /** Sidik-sidik metode */
}

Berikut merupakan suatu contoh antarmuka:

public interface Edible {
  /** Menjelaskan bagaimana memakan */
  public abstract String bagaimanaMemakan();
}

Antarmuka diperlakukan seperti kelas dalam JAVA. Setiap antarmuka dikompilasi menjadi file bytecode terpisah, sama seperti kelas biasa. Seperti kelas abstrak, Anda tidak bisa menciptakan suatu instans dari antarmuka menggunakan operator new, tetapi pada banyak kasus Anda dapat menggunakan antarmuka kurang lebih sama dengan ketika Anda menggunakan kelas abstrak. Sebagai contoh, Anda dapat menggunakan antarmuka sebagai tipe data untuk suatu variabel referensi, sebagai hasil casting, dan seterusnya.

Anda sekarang dapat menggunakan antarmuka Edible untuk menentukan apakah suatu objek dapat dimakan atau tidak. Ini dilakukan dengan mengijinkan kelas agar objek mengimplementasikan antarmuka ini menggunakan katakunci implements. Sebagai contoh, kelas Ayam dan kelas Buah pada kode1.6 (baris 14, 23) mengimplementasikan antarmuka Edible. Relasi antara kelas dan antarmuka dikenal dengan pewarisan antarmuka. Karena pewarisan antarmuka dan pewarisan kelas secara esensi sama, maka keduanya disebut dengan pewarisan saja.

Kode1.6 UjiEdible.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class UjiEdible {
  public static void main(String[] args) {
    Object[] objek = {new Harimau(), new Ayam(), new Apel()};
    for (int i = 0; i < objek.length; i++)
      if (objek[i] instanceof Edible)
        System.out.println(((Edible)objek[i]).bagaimanaMemakan());
  }
}

class Binatang {
 // Bidang data, konstruktor, dan metode diabaikan
}

class Ayam extends Binatang implements Edible {
  public String bagaimanaMemakan() {
    return "Ayam: Goreng saja";
  }
}

class Harimau extends Binatang {
}

abstract class Buah implements Edible {
  // Bidang data, konstruktor, dan metode diabaikan
}

class Apel extends Buah {
  public String bagaimanaMemakan() {
    return "Apel: Kupas dan makan";
  }
}

class Jeruk extends Buah {
  public String bagaimanaMemakan() {
    return "Jeruk: Dibuat jus";
  }
}

Keluaran
Ayam: Goreng saja
Apel: Kupas dan makan

Kelas Ayam mewarisi Binatang dan mengimplementasikan Edible untuk menentukan apakah ayam dapat dimakan atau tidak. Ketika suatu kelas mengimplementasikan antarmuka, maka semua metode yang didefinisikan di dalam antarmuka diimplementasikan dengan sidik dan tipe nilai balik yang persis sama. Kelas Ayam mengimplementasikan metode bagaimanaMemakan() (baris 15-17).

Kelas Buah mengimplementasikan Edible. Karena kelas Buah tidak mengimplementasikan metode bagaimanaMemakan(), maka Buah harus didefinisikan abstract (baris 23). Sub-subkelas konkrit dari Buah harus mengimplementasikan metode bagaimanaMemakan(). Kelas Apel dan Jeruk mengimplementasikan metode bagaimanaMemakan() (baris 28, 34).


1.5 Contoh: Antarmuka Comparable
Dimisalkan Anda ingin mendesain suatu metode generik untuk mencari mana yang lebih besar dari dua objek yang bertipe sama, seperti dua mahasiswa, dua tanggal, dua lingkaran, atau dua persegi-panjang. Untuk melakukannya, dua objek harus komparabel atau dapat dibandingkan. Jadi, watak umum objek adalah komparabel. JAVA menyediakan antarmuka Comparable untuk tujuan ini. Antarmuka ini didefinisikan sebagai berikut:

// Antarmuka untuk membandingkan objek, didefinisikan di dalam java.lang
package java.lang;

public interface Comparable {
  public int compareTo(Object o);
}

Metode compareTo menentukan urutan objek ini dengan objek tertentu o dan menghasilkan suatu integer negatif, nol, atau positif jika objek ini kurang dari, sama dengan, atau lebih besar dari o.

Banyak kelas dalam pustaka JAVA (misalnya, String dan Date) mengimplementasikan antarmuka Comparable untuk menentukan urutan objek-objek. Jika Anda memeriksa kode-kode kelas ini, Anda akan menemukan katakunci implements digunakan, seperti ditunjukkan di bawah ini:



Jadi, watak string adalah komparabel, dan begitu juga dengan tanggal.Dimisalkan s adalah suatu objek String dan d adalah suatu objek Date, maka semua ekspresi berikut adalah true:



Suatu metode generik, max, untuk mencari yang lebih besar dari dua objek dapat didefinisikan seperti pada (a) atau (b) di bawah ini:



Metode max pada (a) lebih sederhana dari (b). Dalam kelas Max pada (b), o1 dideklarasikan sebagi Object, dan (Comparable).o1 memberitahukan kepada kompiler untuk melakukan casting terhadap o1 menjadi Comparable sehingga metode compareTo dapat dipanggil dari o1.

Metode max pada (a) lebih handal dari (b). Anda harus memanggil metode max dengan dua objek yang komparabel. Seandainya Anda memanggil metode max dengan dua objek non-komparabel:

Max.max(sembarangObjek1, sembarangObjek2);

Kompiler akan mendeteksi error menggunakan metode max pada (a), karena sembarangObjek1 bukan suatu instans dari Comparable. Menggunakan metode max pada (b), kode tersebut akan baik-baik saja, tetapi akan mengalami ClassCastException, karena sembarangObjek1 bukan suatu instans dari Comparable dan tidak bisa dilakukan casting menjadi Comparable.

Mulai dari sekarang, diasumsikan bahwa metode max pada (a) yang digunakan. Karena string dan tanggal adalah komparabel, maka Anda dapat menggunakan metode max untuk mencari yang lebih besar dari dua instans String dan Date. Berikut adalah salah satu contohnya:



Nilai return pada metode max bertipe Comparable. Jadi, Anda melakukan casting terhadap nilai return menjadi String atau Date secara eksplisit.

Anda tidak bisa menggunakan metode max untuk mencari yang lebih besar dari dua instans PersegiPanjang, karena PersegiPanjang tidak mengimplementasikan Comparable. Instans-instans kelas baru ini adalah komparabel. Kelas baru ini diberi nama PersegiPanjangKomparabel, seperti ditampilkan pada kode1.7:


Kode1.7 PersegiPanjangKomparabel.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class PersegiPanjangKomparabel extends PersegiPanjang
    implements Comparable{
  /** Menciptakan suatu PersegiPanjangKomparabel dengan properti tertentu */
  public PersegiPanjangKomparabel(double lebar, double tinggi) {
    super(lebar, tinggi);
  }

  /** Mengimplementasikan metode compareTo didefinisikan dalam Comparable */
  public int compareTo(Object o){
    if (dapatLuas() > ((PersegiPanjangKomparabel)o).dapatLuas())
      return 1;
    else if (dapatLuas() < ((PersegiPanjangKomparabel)o).dapatLuas())
      return -1;
    else
      return 0;
  }
}

PersegiPanjangKomparabel mewarisi PersegiPanjang dan mengimplementasikan Comparable, seperti ditampilkan pada Gambar 1.4. Katakunci implements mengindikasikan bahwa PersegiPanjangKomparabel mewarisi semua konstanta dari antarmuka Comparabel dan mengimplementasikan semua metodenya. Suatu instans PersegiPanjangKomparabel juga merupakan instans dari PersegiPanjang, ObjekGeometri, Object, dan Comparable.


Gambar 1.4 PersegiPanjangKomparabel mewarisi PersegiPanjang dan mengimplementasikan Comparable


Anda sekarang dapat menggunakan max untuk yang lebih besar dari dua objek PersegiPanjangKomparabel. Berikut adalah salah satu contohnya:

PersegiPanjangKomparabel persegipanjang1 = new PersegiPanjangKomparabel (4, 5);
PersegiPanjangKomparabel persegipanjang12= new PersegiPanjangKomparabel (3, 6);
System.out.println(Max.max(persegipanjang1, persegipanjang2));

Suatu antarmuka menyediakan suatu bentuk lain dari pemrograman generik. Akan sangat sulit bila menggunakan suatu metode generik max untuk mencari objek maksimum tanpa menggunakan antarmuka pada contoh ini, karena pewarisan jamak perlu untuk mewarisi Comparable dan kelas lain, seperti PersegiPanjang, pada saat yang sama.

Kelas Object memiliki metode equals, yang ditujukan bagi sub-subkelas Objects untuk mengoverride untuk memeriksa apakah dua objek sama atau tidak. Dimisalkan kelas Object mempunyai metode compareTo, seperti didefinisikan pada antarmuka Comparable, maka metode max dapat digunakan untuk membandingkan suatu daftar yang berisi objek-objek. Apakah metode compareTo seharusnya disertakan dalam kelas Object itu masih bisa diperdebatkan. Karena metode compareTo tidak didefinisikan di dalam Object, maka antarmuka Comparable didefinisikan di dalam JAVA agar objek-objek dapat dibandingkan jika objek-objek merupakan instans dari antarmuka Comparable. Sangat direkomendasikan bahwa compareTo harus konsisten dengan equals. Yaitu, untuk dua objek o1 dan o2, o1.compareTo(o2) == 0 jika dan hanya jika o1.equals(o2) bernilai true.


1.6 Contoh: Antarmuka ActionListener
Sekarang Anda telah siap untuk menulis suatu program singkat untuk menjawab tantangan yang diberikan pada awal bab ini. Program menampilkan dua tombol di dalam frame, seperti ditampilkan pada Gambar 1.1. Untuk merespon suatu pengklikan tombol, Anda perlu menulis kode untuk memproses aksi pengklikan-tombol. Tombol merupakan objek sumber atau source object dimana aksi berasal. Anda perlu menciptakan suatu objek yang dapat menangani aksi penekanan-tombol tersebut. Objek ini disebut dengan suatu listener, seperti tertampil pada 1.5.

Tidak semua objek dapat menjadi listener dari antarmuka ActionListener. Ada dua syarat agar objek bisa menjadi listener:
·         Objek harus merupakan instans dari antarmuka ActionListener. Antarmuka ini mendefinisikan watak umum untuk semua aksi listener.
·         Objek ActionListener, pendengar, harus terdaftar dengan sumber menggunakan metode sumber.addActionListener(pendengar).



Gambar 1.5 Objek listener memproses event atau kejadian yang dipicu dari objek sumber


Antarmuka ActionListener memuat metode actionPerformed untuk memproses event.Kelas listener Anda harus mengoverride metode ini untuk merespon kejadian. Kode1.8 menyajikan suatu contoh kode yang memproses ActionEvent pada dua tombol. Ketika Anda menekan tombol OK, pesan “Tombol OK diklik”ditampilkan. Ketika Anda menekan tombol Batak, pesan “Tombol Batal diklik”ditampilkan, seperti ditampilkan pada Gambar 1.1.

Kode1.8 PenangananEvent.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import javax.swing.*;
import java.awt.event.*;

public class PenangananEvent extends JFrame{
  public PenangananEvent() {
    // Menciptakan dua tombol
    JButton jbtOK = new JButton("OK");
    JButton jbtBatal = new JButton("Batal");

    // Menciptakan panel yang menampung tombol
    JPanel panel = new JPanel();
    panel.add(jbtOK);
    panel.add(jbtBatal);

    add(panel); // Menambahkan panel ke dalam frame

    // Mendaftarkan listener
    OKListenerClass listener1 = new OKListenerClass();
    CancelListenerClass listener2 = new CancelListenerClass();
    jbtOK.addActionListener(listener1);
    jbtBatal.addActionListener(listener2);
  }

  public static void main(String[] args) {
    JFrame frame = new PenangananEvent();
    frame.setTitle("Penanganan Event");
    frame.setSize(200, 150);
    frame.setLocation(200, 100);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
  }
}

class OKListenerClass implements ActionListener{
  public void actionPerformed(ActionEvent e) {
    System.out.println("Tombol OK diklik");
  }
}

class CancelListenerClass implements ActionListener{
  public void actionPerformed(ActionEvent e) {
    System.out.println("Tombol Batal diklik");
  }
}

Dua kelas listener didefinisikan pada baris 34-44. Setiap kelas listener mengimplementasikan ActionListener untuk memproses ActionEvent. Objek listener1 merupakan instans dari OKListenerClass (baris 18), yang didaftarkan dengan tombol jbtOK (baris 20). Ketika tombol OK ditekan, metode actionPerformed(ActionEvent) (baris 36) dalam OKListenerClass dipanggil untuk memproses event. Objek listener2 merupakan instans dari CancelListenerClass (baris 19), yang didaftarkan dengan tombol jbtBatal (baris 21). Ketika tombol Batal ditekan, metode actionPerformed(ActionEvent) (baris 42) dalam CancelListenerClass dipanggil untuk memproses event.

1.7 Contoh: Antarmuka Cloneable
Seringkali diinginkan untuk menciptakan salinan dari suatu objek. Anda perlu menggunakan metode clone dan memahami antarmuka Cloneable, yang merupakan topik bahasan pada bagian ini.
Antarmuka biasanya memuat konstanta-konstanta dan metode-metode abstrak, tetapi antarmuka Cloneable merupakan kasus spesial. Antarmuka Cloneable dalam paket java.lang didefinisikan sebagai berikut:

package java.lang;

public interface Cloneable {
}

Antarmuka ini kosong. Suatu antarmuka kosong disebut dengan antarmuka penanda atau marker interface. Antarmuka penanda tidak memuat konstanta dan metode. Antarmuka kosong digunakan untuk menandai kelas yang memiliki properti khusus. Suatu kelas yang mengimplementasikan antarmuka Cloneable dikatakan klonabel, dan objeknya diklon menggunakan metode clone() yang didefinisikan dalam kelas Object.

Banyak kelas dalam pustaka JAVA (misalnya, Calendar, Date, dan ArrayList) mengimplementasikan Cloneable. Jadi, instans-instans dari kelas-kelas tersebut dapat diklonkan. Misalnya, kode berikut

1 Calendar kalender = new GregorianCalendar(2003, 2, 1);
2 Calendar kalender1 = kalender;
3 Calendar kalender2 = (Calendar) kalender.clone();
4 System.out.println("kalender == kalender1 adalah " +
5  (kalender == kalender1));
6 System.out.println("kalender == kalender2 adalah " +
7  (kalender == kalender2));
8 System.out.println("kalendar.equals(kalendar2) adalah " +
9  kalender.equals(kalender2));

menampilkan

kalender == kalender1 adalah true
kalender == kalender2 adalah false
kalender.equals(kalender2) adalah true

Pada kode sebelumnya, baris 2 menyalin referensi dari kelender kepada kalender1, jadi kalender dan kelender1 sama-sama menunjuk kepada objek Calendar yang sama. Baris 3 menciptakan suatu objek baru yang merupakan hasil klon dari kalender dan menugaskan referensi dari objek baru itu kepada kalender2. Sekarang, kalender2 dan kalender merupakan dua objek yang berbeda yang memiliki isi sama.

Anda bisa mengklon suatu array menggunakan metode clone(). Sebagai contoh, kode berikut

1 int[] list1 = {1, 2};
2 int[] list2 = list1.clone();
3 list1[0] = 7; list1[1] = 8;
4
5 System.out.println("list1 adalah " + list1[0] + ", " + list1[1]);
6 System.out.println("list2 adalah " + list2[0] + ", " + list2[1]);

menampilkan
list1 adalah 7, 8
list2 adalah 1, 2

Untuk mendefinisikan kelas yang mengimplementasikan antarmuka Cloneable, kelas harus mengoverride metode clone() dalam kelas Object. Kode1.9 mendefinisikan suatu kelas yang diberinama Rumah yang mengimplementasikan Cloneable dan Comparable.

Kode1.9 Rumah.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Rumah implements Cloneable, Comparable{
  private int id;
  private double luas;
  private java.util.Date kapanDibangun;

  public Rumah(int id, double luas) {
    this.id = id;
    this.luas = luas;
    kapanDibangun = new java.util.Date();
  }

  public int dapatId() {
    return id;
  }

  public double dapatLuas() {
    return luas;
  }

  public java.util.Date dapatKapanDibangun() {
    return kapanDibangun;
  }

  /** Mengoverride metode klon protected yang didefinisikan dalam
      kelas Object, dan memperkuat aksesibilitasnya */
  public Object clone() throws CloneNotSupportedException {
    return super.clone();
  }

  /** Mengimplementasikan metode compareTo,didefinisikan dalam Comparable */
  public int compareTo(Object o) {
    if (luas > ((Rumah)o).luas)
      return 1;
    else if (luas < ((Rumah)o).luas)
      return -1;
    else
      return 0;
  }
}

Kelas Rumah mengimplementasikan metode clone (baris 26-28) yang didefinisikan dalam kelas Object. Headernya adalah

protected native Object clone() throws CloneNotSupportedException;

Katakunci native mengindikasikan bahwa metode ini tidak ditulis dalam JAVA tetapi diimplementasikan dalam JVM untuk platform natif. Katakunci protected membatasi metode agar hanya bisa diakses dari sesama paket atau dari subkelas. Karena alasan ini, kelas Rumah harus mengoverride metode clone menjadi public agar bisa diakses dari sembarang paket. Karena metode clone yang diimplementasikan untuk platform natif dalam kelas Object melakukan tugas pengklonan objek, maka metode clone di dalam kelas Rumah hanya ditujukan untuk memanggil super.clone(). Metode clone yang didefinisikan dalam kelas Object bisa saja melempar CloneNotSupportedException.

Kelas Rumah mengimplementasikan metode compareTo (baris 31-38) yang didefinisikan dalam kelas Comparable. Metode ini membandingkan luas dua rumah.


Gambar 1.6 Metode clone menyalin nilai-nilai bidang-bidang bertipe primitif dan referensi-referensi bidang-bidang bertipe objek.


Anda sekarang dapat menciptakan suatu objek dari kelas Rumah dan menciptakan suatu salinan identik dari objek tersebut, sebagai berikut:

Rumah rumah1 = new Rumah (1, 1750.50);
Rumah rumah2 = (House) rumah1.clone();

rumah1 dan rumah2 adalah dua objek berbeda dengan isi yang identik. Metode clone dalam kelas Object menyalin setiap bidang dari objek asli kepada objek target. Jika bidang bertipe primitif, maka nilainya akan disalin. Sebagai contoh, nilai luas (bertipe double) disalin dari rumah1 kepada rumah2. Jika bidang bertipe objek, maka referensi bidang tersebut disalin. Sebagai contoh, bidang kapanDiciptakan bertipe Date, jadi referensinya disalin kepada rumah, seperti ditunjukkan pada Gambar 1.6. Oleh karena itu, rumah1.kapanDibangun == rumah2.kapanDibangun adalah true, meskipun rumah1 == rumah2 adalah false. Hal ini dikenal dengan penyalinan dangkal atau shallow copy, bukan penyalinan dalam atau deep copy, yang berarti bahwa jika bidang bertipe objek, maka referensi objek yang disalin, bukan isinya yang disalin.


1.8 Antarmuka versus Kelas Abstrak
Antarmuka dapat digunakan kurang lebih sama dengan kelas abstrak, tetapi pendefinisian antarmuka berbeda dari pendefinisian kelas abstrak. Tabel1.2 menyimpulkan perbedaan-perbedaan antara keduanya.

JAVA hanya mengijinkan pewarisan tunggal untuk pewarisan kelas tetapi mengijinkan pewarisan jamak untuk antarmuka. Sebagai contoh,

public class  KelasBaru  extends  KelasBasis
  implements  Antarmuka1, ..., AntarmukaN{
  ...
}

Tabel 1.2 Antarmuka versi Kelas Abstrak

Variabel
Konstruktor
Metode
Kelas abstrak







Antarmuka
Tidak ada pembatasan






Semua variabel harus public static final
Konstruktor dipanggil oleh subkelas melalui rantai konstruktor. Kelas abstrak tidak bisa diinstansiasi menggunakan operator new.

Tidak ada konstruktor. Antarmuka tidak bisa diinstansiasi menggunakan operator new.
Tidak Ada pembatasan






Semua metode harus berupa metode instans public abstract


Antarmuka dapat mewarisi antarmuka lain menggunakan katakunci extends. Antarmuka seperti itu disebut dikenal dengan sub-antarmuka atau subinterface. Misalnya, AntarmukaBaru pada kode berikut ini merupakan sub-antarmuka Antarmuka1, ..., dan AntarmukaN.

public interface  AntarmukaBaru  extends Antarmuka1, ..., AntarmukaN {
  // konstanta-konstanta dan metode-metode abstrak
}

Suatu kelas yang mengimplementasikan AntarmukaBaru harus mengimplementasikan metode-metode abstrak yang didefinisikan dalam  AntarmukaBaru, Antarmuka1 ,..., dan AntarmukaN. Suatu antarmuka dapat mewarisi antarmuka-antarmuka lain, tetapi tidak mewarisi kelas-kelas lain. Suatu kelas dapat mewarisi superkelasnya dan mengimplementasikan antarmuka-antarmuka.
Semua kelas memiliki akar yang sama, kelas Object, tetapi tidak ada akar bagi antarmuka. Seperti kelas, antarmuka juga mendefinisikan tipe. Variabel dari suatu tipe antarmuka dapat menunjuk kepada sembarang instans dari kelas yang mengimplementasikan antarmuka. Jika suatu kelas mengimplementasikan suatu antarmuka, maka antarmuka itu seperti superkelas bagi kelas tersebut. Anda dapat menggunakan antarmuka sebagai tipe data dan melakukan casting terhadap variabel bertipe antarmuka menjadi bertipe subkelasnya, dan sebaliknya. Sebagai contoh, dimisalkan bahwa c adalah suatu instans dari Kelas2 pada Gambar 1.7, maka c juga merupakan instans dari Object, Kelas1, Interface1, Interface1_1, Interface1_2, Interface2_1, dan Interface2_2.


Gambar 1.7 Kelas1 mengimplementasikan Interface1; Interface1 mewarisi Interface1_1 dan Interface1_2. Kelas2 mewarisi Kelas1 dan mengimplementasikan Interface2_1 dan Interface2_2


Pada umumnya, antarmuka lebih diinginkan dari kelas abstrak karena antarmuka dapat mendefinisikan suatu supertipe umum untuk kelas-kelas yang tidak berelasi. Antarmuka lebih fleksibel dari kelas. Pertimbangkan kelas Binatang. Dimisalkan bahwa metode bagaimanaMemakan didefinisikan di dalam kelas Binatang, sebagai berikut:

abstract class  Binatang {
  public abstract String bagaimanaMemakan();
}

Dua subkelas dari Binatang didefinisikan sebagai berikut:

class  Ayam extends Binatang {
  public String bagaimanaMemakan (){
    return "Goreng saja";
  }
}

class  Bebek extends Binatang {
  public String bagaimanaMemakan(){
    return "Panggang saja";
  }
}

Dengan hirarki pewarisan ini, polimorfisme memampukan Anda untuk memuat suatu referensi kepada suatu objek Ayam atau objek Bebek di dalam suatu variabel bertipe Binatang, seperti ditunjukkan pada kode berikut ini:

public static void main(String[] args) {
  Binatang binatang = new Ayam();
  makan(binatang);

  animal = new Bebek();
  makan(binatang);
}

public static void makan(Binatang binatang) {
  binatang.bagaimanaMemakan();
}

JVM secara dinamis memutuskan metode bagaimanaMemakan() mana yang dipanggil berdasar pada objek aktual yang memanggil metode.

Antarmuka tidak memiliki keterbatasan. Antarmuka memberikan Anda fleksibilitas yang lebih daripada kelas, karena Anda tidak harus membuat segala sesuatu sesuai dengan suatu tipe kelas. Anda bisa mendefinisikan metode bagaimanaMemakan di dalam suatu antarmuka dan membiarkannya berperan sebagai supertipe bersama untuk kelas-kelas lain. Sebagai contoh,

public static void main(String[] args) {
  Edible makanan = new Ayam();
  makan(makanan);

  makanan = new Bebek();
  makan (makanan);

  makanan = new Brokoli();
  makan (makanan);
}

public static void eat(Edible makanan) {
  makanan.bagaimanaMemakan();
}

interface Edible {
  public String bagaimanaMemakan ();
}

class  Ayam implements Edible {
  public String bagaimanaMemakan(){
    return "Goreng saja";
  }
}

class Bebek implements Edible {
  public String bagaimanaMemakan (){
    return "Panggang saja";
  }
}

class Brokoli implements  Edible {
  public String bagaimanaMemakan (){
    return "Rebus saja";
  }
}


1.9 Memproses Nilai Bertipe Data Primitif Sebagai Objek
JAVA menyediakan kelas-kelas wrapper,seperti Boolean, Character, Double, Float, Byte, Short, Integer, dan Long untuk tipe-tipe primitif. Kelas-kelas ini dikelompokkan dalam paket java.lang. Hirarki pewarisannya ditampilkan pada Gambar 1.8.


Gambar 1.8 Kelas Number adalah superkelas abstrak bagi Double, Float, Byte, Short, Integer, dan Long


Setiap kelas wrapper numerik mewarisi kelas abstrak Number, yang memuat metode-metode doubleValue(), floatValue(), intValue(), longValue(), shortValue(), dan byteValue(). Metode-metode ini mengkonversi objek menjadi nilai bertipe primitif. Setiap kelas wrapper mengoverride metode equals dan metode toString yang didefinisikan di dalam kelas Object. Karena semua kelas wrapper mengimplementasikan antarmuka Comparable, maka metode compareTo diimplementasikan dalam kelas-kelas tersebut.


Gambar 1.9 Kelas-kelas wrapper menyediakan konstruktor,konstanta, dan metode konversi untuk memanipulasi berbagai tipe data

Kelas-kelas wrapper mirip satu sama lain. Bagian ini akan menggunakan Integer dan Double sebagai contoh untuk mengenalkan kelas wrapper numerik. Fitur-fitur kunci dari Integer dan Double ditampilkan pada Gambar 1.9.

Anda bisa menciptakan suatu objek wrapper dari nilai tipe data primitif atau dari string yang merepresentasikan nilai numerik, misalnya new Double(5.0), new Double(“5.0”), dan new Integer(“5”).

Kelas wrapper tidak memiliki konstruktor tanpa-argumen. Instans dari semua kelas wrapper adalah immutable; ini berarti bahwa begitu objek diciptakan, maka nilai internalnya tidak dapat diubah.

Setiap kelas wrapper numerik memiliki konstanta MAX_VALUE dan MIN_VALUE. MAX_VALUE merepresentasikan nilai maksimum dari tipe data yang diberikan. Untuk Byte, Short, Integer, dan Long, MIN_VALUE merepresentasikan nilai minimum dari byte, short, int, dan long. Untuk Float dan Double, MIN_VALUE merepresentasikan nilai positif minimum dari float dan double. Statemen-statemen berikut ini menampilkan integer maksimum (2.147.283.647), nilai minimum positif dari float (1.4E-45), dan nilai pecahan maksimum double (1.79769313486231570e+308d)

System.out.println("Integer maksimum adalah " + Integer.MAX_VALUE);
System.out.println("float positif minimum adalah " + Float.MIN_VALUE);
System.out.println("Pecahan maksimum double adalah " + Double.MAX_VALUE);

Setiap kelas wrapper numerik mengimplementasikan metode-metode abstrak doubleValue(), floatValue(), intValue(), longValue(), dan shortValue(), yang didefinisikan di dalam kelas Number. Metode-metode ini mengembalikan nilai double, float, int, long, atau short untuk objek wrapper.

Kelas-kelas wrapper numerik memiliki suatu metode statik yang bermanfaat, valueOf(String s). Metode ini menciptakan suatu objek baru yang diinisialisasi dengan nilai yang direpresentasikan oleh string. Sebagai contoh,

Double objekDouble = Double.valueOf("12.4");
Integer objekInteger = Integer.valueOf("12");

Anda dapat menggunakan metode parseInt di dalam kelas Integer untuk mengkonversi suatu string numerik menjadi nilai int dan metode parseDouble di dalam kelas Double untuk mengkonversi suatu string numerik menjadi nilai double. Setiap kelas wrapper numerik memiliki dua metode parsing teroverload untuk mengkonversi suatu string numerik menjadi nilai numerik sesuai, berbasis 10 (desimal) atau berbasis sembarang radiks (misalnya 2 untuk biner, 8 untuk oktal, 16 untuk heksadesimal). Metode-metode ini ditunjukkan berikut ini:

// Kedua metode ini dalam kelas Byte
public static byte parseByte(String s)
public static byte parseByte(String s, int radix)
// Kedua metode ini dalam kelas Short
public static short parseShort(String s)
public static short parseShort(String s, int radix)

// Kedua metode ini dalam kelas Integer
public static int parseInt(String s)
public static int parseInt(String s, int radix)

// Kedua metode ini dalam kelas Long
public static long parseLong(String s)
public static long parseLong(String s, int radix)

// Kedua metode ini dalam kelas Float
public static float parseFloat(String s)
public static float parseFloat(String s, int radix)

// Kedua metode ini dalam kelas Double
public static double parseDouble(String s)
public static double parseDouble(String s, int radix)

Sebagai contoh,

Integer.parseInt("11", 2) mengembalikan 3;
Integer.parseInt("12", 8) mengembalikan 10;
Integer.parseInt("13", 10) mengembalikan 13;
Integer.parseInt("1A", 16) mengembalikan 26;

Integer.parseInt(“12”,2) akan menghasilkan suatu eksepsi runtime karena 12 bukan suatu angka biner.

1.10 Mengurutkan Array Objek
Contoh ini menyajikan suatu metode statik generik untuk mengurutkan suatu array yang memuat objek-objek komparabel. Objek-objek tersebut merupakan instans-instans dari antarmuka Comparable, dan dibandingkan menggunakan metode compareTo. Metode tersebut dapat digunakan untuk mengurutkan array yang memuat sembarang objek sepanjang kelasnya mengimplementasikan antarmuka Comparable.

Untuk menguji metode tersebut, program mengurutkan array integer, array double, array karakter, dan array string. Program ditampilkan pada Gambar 1.10.

Kode1.10 UrutGenerik.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class UrutGenerik {
  public static void main(String[] args) {
    // Menciptakan suatu array Integer
    Integer[] intArray = {new Integer(2), new Integer(4),
      new Integer(3)};

    // Menciptakan suatu array Double
    Double[] doubleArray = {new Double(3.4), new Double(1.3),
      new Double(-22.1)};

    // Menciptakan suatu array Character
    Character[] charArray = {new Character('a'),
      new Character('J'), new Character('r')};

    // Menciptakan suatu array String
    String[] stringArray = {"Vivian", "Kristina", "Butet"};

    // Mengurutkan array
    urut(intArray);
    urut(doubleArray);
    urut(charArray);
    urut(stringArray);

    // Menampilkan array terurut
    System.out.print("Objek-objek Integer terurut: ");
    printList(intArray);
    System.out.print("Objek-objek Double terurut: ");
    printList(doubleArray);
    System.out.print("Objek-objek Character terurut: ");
    printList(charArray);
    System.out.print("Objek-objek String terurut: ");
    printList(stringArray);
  }

  /** Mengurutkan array yang memuat objek-objek komparabel */
  public static void urut(Comparable[] list){
    Comparable minSekarang;
    int indeksMinSekarang;

    for (int i = 0; i < list.length - 1; i++) {
      // Menemukan maksimum dalam list[0..i]
      minSekarang = list[i];
      indeksMinSekarang = i;

      for (int j = i + 1; j < list.length; j++) {
        if (minSekarang.compareTo(list[j]) > 0) {
          minSekarang = list[j];
          indeksMinSekarang = j;
        }
      }

      // Menukar list[i] dengan list[indeksMinSekarang] jika diperlukan;
      if (indeksMinSekarang != i) {
        list[indeksMinSekarang] = list[i];
        list[i] = minSekarang;
      }
    }
  }

  /** Menampilkan array objek */
  public static void printList(Object[] list) {
    for (int i = 0; i < list.length; i++)
      System.out.print(list[i] + " ");
    System.out.println();
  }
}

Keluaran

Objek-objek Integer terurut: 2 3 4
Objek-objek Double terurut: -22.1 1.3 3.4
Objek-objek Character terurut: J a r
Objek-objek String terurut: Butet Kristina Vivian

Algoritma untuk metode urut cukup sederhana, yang mengurutkan array yang memuat sembarang tipe objek, jika dan hanya jika objek-objek tersebut merupakan instans dari antarmuka Comparable.Ini merupakan contoh dari pemrograman generik. Pemrograman generik memampukan metode untuk beroperasi pada argumen-argumen bertipe generik, yang membuatnya dapat didaur-ulang dengan berbagai tipe.

Kelas-kelas Integer, Double, Character, dan String mengimplementasikan Comparable, sehingga objek-objek dari kelas-kelas tersebut dapat dibandingkan menggunakan metode compareTo. Metode urut menggunakan metode compareTo untuk menentukan urutan objek-objek di dalam array.

JAVA menyediakan suatu metode statik sort untuk mengurutkan suatu array yang memuat sembarang tipe objek di dalam kelas java.util.Array, jika dan hanya jika semua elemen array adalah komparabel. Jadi, Anda bisa menggunakan kode berikut ini untuk mengurutkan array:

java.util.Arrays.sort(intArray);
java.util.Arrays.sort(doubleArray);
java.util.Arrays.sort(charArray);
java.util.Arrays.sort(stringArray);

Array adalah objek. Suatu array merupakan instans dari kelas Object.Jadi, jika A merupakan subtipe dari B, maka setiap instans dari A[] merupakan instans dari B[]. Oleh karena itu, statemen-statemen berikut ini adalah true:

new int[10] instanceof Object
new Integer[10] instanceof Object
new Integer[10] instanceof Comparable[]
new Integer[10] instanceof Number[]
new Number[10] instanceof Object[]


1.11 Konversi Otomatis Antara Tipe Primitif dan Tipe Kelas Wrapper
JAVA mengijinkan tipe-tipe primitif dan tipe-tipe kelas wrapper untuk dikonversi secara otomatis. Sebagai contoh, statemen pada (a) dapat disederhanakan menjadi statemen pada (b) karena autoboxing:


Konversi antara tipe primitif menjadi tipe objek wrapper dikenal dengan autoboxing. Konversi sebaliknya disebut dengan unboxing. Perhatikan contoh sebagai berikut:

1 Integer[] intArray = {1, 2, 3};
2 System.out.println(intArray[0] + intArray[1] + intArray[2]);

Pada baris 1, nilai-nilai primitif 1, 2, dan 3 secara otomatis dikonversi menjadi objek-objek new Integer(1), new Integer(2), dan new Integer(3). Pada baris 2, objek-objek intArray[0], intArray[1], dan intArray[2] secara otomatis dikonversi menjadi nilai-nilai int  yang akan ditambahkan bersama.


1.12 Kelas BigInteger dan BigDecimal
Jika Anda perlu menghitung angka-angka integer sangat besar atau nilai-nilai pecahan presisi-tinggi, maka Anda dapat menggunakan kelas BigInteger dan BigDecimal di dalam paket java.math. Keduanya adalah kelas immutable. Kedua kelas tersebut mewarisi kelas Number dan mengimplementasikan antarmuka Comparable. Integer terbesar tipe long adalah Long.MAX_VALUE (9223372036854775807). Suatu instans dari BigInteger dapat merepresentasikan integer dengan sembarang ukuran. Anda sekarang dapat menggunakan new BigInteger(String) dan new BigDecimal(String) untuk menciptakan instans dari BigInteger dan BigDecimal, menggunakan metode-metode add, subtract, multiple, divide, dan remainder untuk melakukan operasi-operasi aritmatik, dan menggunakan metode compareTo untuk membandingkan dua integer besar. Sebagai contoh, kode berikut menciptakan dua objek BigInteger dan mengalikan keduanya:

BigInteger a = new BigInteger("9223372036854775807");
BigInteger b = new BigInteger("2");
BigInteger c = a.multiply(b); // 9223372036854775807 * 2
System.out.println(c);

Keluarannya adalah 18446744073709551614.

Tidak ada batasan kepresisian untuk objek BigDecimal. Metode divide dapat melemparkan eksepsi ArithmeticException jika hasil tidak dapat dihentikan. Akan tetapi, Anda bisa menggunakan metode teroverload divide(BigDecimal d, int skala, int modePembulatan) untuk menentukan skala dan mode pembulatan untuk menghindari eksepsi ini, dimana skala adalah jumlah dijit minimum setelah titik desimal. Sebagai contoh, kode berikut menciptakan dua objek BigDecimal dan melakukan pembagian dengan skala 20 dan mode pembulatan BigDecimal.ROUND_UP.

BigDecimal a = new BigDecimal(1.0);
BigDecimal b = new BigDecimal(3);
BigDecimal c = a.divide(b, 20, BigDecimal.ROUND_UP);
System.out.println(c);

Keluarannya adalah 0.33333333333333333334. Perhatikan bahwa hasil faktorial atas suatu integer dapat menjadi sangat besar. Kode1.11 memberikan suatu metode untuk menghasilkan faktorial atas sembarang integer.

Kode1.11 FaktorialBesar.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.math.*;

public class FaktorialBesar {
  public static void main(String[] args) {
    System.out.println("50! adalah \n" + faktorial(50));
  }

  public static BigInteger faktorial(long n) {
    BigInteger result = BigInteger.ONE;
    for (int i = 1; i <= n; i++)
      result = result.multiply(new BigInteger(i + ""));

    return result;
  }
}

Keluaran

50! adalah
30414093201713378043612608166064768844377641568960512000000000000

BigInteger.ONE (baris 9) adalah suatu konstanta yang didefinisikan di dalam kelas BigInteger. BigInteger.ONE sama dengan new BigInteger(“1”).

1.13 Studi Kasus: Kelas Rasional
Suatu bilangan rasional memiliki pembilang dan penyebut dalam format a/b, dimana a adalah pembilang dan b adalah penyebut. Sebagai contoh, 3/4, 6/7, dan 9/8 adalah angka-angka rasional.

Suatu bilangan rasional tidak dapat memiliki penyebut 0, tetapi jika pembilangnya 0 tidak masalah. Setiap integer i ekivalen dengan angka rasional i/1. Bilangan rasional dipakai dalam komputasi eksak yang melibatkan pecahan, misalnya 1/3 = 0.3333333.... Angka ini tidak bisa secara presisi direpresentasikan dalam format pecahan menggunakan tipe data float ataupun double. Untuk mendapatkan hasil yang eksak, digunakan angka rasional.

JAVA menyediakan tipe-tipe data integer dan tipe-tipe data pecahan, tetapi tidak menyediakan tipe data rasional. Bagian ini akan menunjukkan bagaimana mendesain suatu kelas yang merepresentasikan bilangan rasional.

Karena bilangan rasional memiliki fitur-fitur yang sama dengan integer dan angka pecahan, dan karena Number merupakan kelas akar bagi kelas-kelas wrapper, maka akan sesuai bila mendefinisikan kelas Rasional menjadi subkelas dari Number. Karena angka rasional adalah komparabel, maka kelas Rasional harus mengimplementasikan antarmuka Comparable. Gambar 1.10 mengilustrasikan kelas Rasional dan relasinya dengan kelas Number dan antarmuka Comparable.

Gambar 1.10 Properti, konstruktor, dan metode untuk kelas Rasional


Ada banyak angka-angka rasional ekivalen, seperti 1/3 = 2/6 = 3/9 = 4/12. Pembagi dan pembilang dari 1/3 tidak memiliki pembagi bersama kecuali 1, jadi 1/3 dikatakan dengan suku terendah.

Untuk mereduksi suatu angka rasional menjadi suku terendahnya, Anda perlu mencari GCD (greates common divisor, pembagi bersama terbesar), kemudian membagi kedua pembilang dan penyebut dengan nilai GCD tersebut. Kode1.12 menyajikan suatu program uji untuk menciptakan dua objek Rasional dan menguji metode-metode yang disediakan.

Kode1.12 UjiKelasRasional.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UjiKelasRasional {
  /** Metode utama */
  public static void main(String[] args) {
    // Menciptakan dan menginisialisasi dua angka rasional r1 dan r2.
    Rasional r1 = new Rasional(4, 2);
    Rasional r2 = new Rasional(2, 3);
 
   // Menampilkan hasil-hasil
   System.out.println(r1 + " + " + r2 + " = " + r1.add(r2));
   System.out.println(r1 + " - " + r2 + " = "+ r1.subtract(r2));
   System.out.println(r1 + " * " + r2 + " = "+ r1.multiply(r2));
   System.out.println(r1 + " / " + r2 + " = "+ r1.divide(r2));
   System.out.println(r2 + " adalah "+ r2.doubleValue());
  }
}

Keluaran

2 + 2/3 = 8/3
2 - 2/3 = 4/3
2 * 2/3 = 4/3
2 / 2/3 = 3
2/3 adalah 0.6666666666666666

Metode main menciptakan dua angka rasional, r1 dan r2 (baris 5-6), dan menampilkan hasil-hasil r1+r2, r1-r2, r1xr2, dan r1/r2 (baris 9-12). Untuk melakukan r1+r2, metode r1.add(r2) dipanggil untuk mengembalikan suatu objek Rasional yang baru. Dengan cara yang sama, r1.subtract(r2) adalah untuk r1-r2, r1.multiply(r2) untuk r1xr2, dan r1.divide(r2) untuk r1/r2.

Metode doubleValue() menampilkan nilai double dari r2 (baris 13). Metode tersebut didefinisikan dalam java.lang.Number dan telah dioverride di dalam Rasional.

Perhatikan bahwa ketika suatu string disambungkan dengan objek menggunakan tanda plus (+), representasi string objek dari metode toString() digunakan untuk menyambung dengan string. Jadi, r1 + “ + " + r2 + " = " + r1.add(r2) ekivalen dengan r1.toString() + " + " + r2.toString() + " = " + r1.add(r2).toString(). Kelas Rasional diimplementasikan dalam kode1.13.

Kode1.13 Rasional.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
public class Rasional extends Number implements Comparable{
  // Bidang-bidang data untuk pembilang dan penyebut
  private long pembilang = 0;
  private long penyebut = 1;

  /** Menciptakan suatu angka rasional dengan properti default */
  public Rasional(){
    this(0, 1);
  }

  /** Menciptakan suatu rasional dengan pembilang and penyebut tertentu*/
  public Rasional(long pembilang, long penyebut){
    long gcd = gcd(pembilang, penyebut);
    this.pembilang = ((penyebut > 0) ? 1 : -1) * pembilang / gcd;
    this.penyebut = Math.abs(penyebut) / gcd;
  }

  /** Menemukan GCD atas dua angka */
  private static long gcd(long n, long d) {
    long n1 = Math.abs(n);
    long n2 = Math.abs(d);
    int gcd = 1;

    for (int k = 1; k <= n1 && k <= n2; k++) {
      if (n1 % k == 0 && n2 % k == 0)
      gcd = k;
    }

    return gcd;
  }

  /** Mengembalikan pembilang */
  public long dapatPembilang() {
    return pembilang;
  }

  /** Mengembalikan penyebut */
  public long dapatPenyebut() {
    return penyebut;
  }

  /** Menambahkan suatu angka rasional kepada rasional ini */
  public Rasional add(Rasional rasionalKedua){
    long n = pembilang * rasionalKedua.dapatPenyebut() +
      penyebut * rasionalKedua.dapatPembilang();
    long d = penyebut * rasionalKedua.dapatPenyebut();
    return new Rasional(n, d);
  }

  /** Mengurangi suatu angka rasional dari rasional ini */
  public Rasional subtract(Rasional rasionalKedua){
    long n = pembilang * rasionalKedua.dapatPenyebut()
      - penyebut * rasionalKedua.dapatPembilang();
    long d = penyebut * rasionalKedua.dapatPenyebut();
    return new Rasional(n, d);
  }

 /** Mengalikan suatu angka rasional dengan rasional ini */
  public Rasional multiply(Rasional rasionalKedua){
    long n = pembilang * rasionalKedua.dapatPembilang();
    long d = penyebut * rasionalKedua.dapatPenyebut();
    return new Rasional(n, d);
 }

  /** Membagi suatu angka rasional dari rasional ini */
  public Rasional divide(Rasional rasionalKedua){
    long n = pembilang * rasionalKedua.dapatPenyebut();
    long d = penyebut * rasionalKedua.pembilang;
    return new Rasional(n, d);
  }

  /** Mengoverride metode toString() */
  public String toString() {
    if (penyebut == 1)
      return pembilang + "";
    else
      return pembilang + "/" + penyebut;
  }

  /** Mengoverride metode equals dalam kelas Object */
  public boolean equals(Object parm1) {
    if ((this.subtract((Rasional)(parm1))).dapatPembilang() == 0)
      return true;
    else
      return false;
  }

  /** Mengimplementasikan metode abstrak intValue dalam java.lang.Number */
  public int intValue() {
    return (int)doubleValue();
  }

  /** Mengimplementasikan metode abstrak floatValue dalam java.lang.Number */
  public float floatValue() {
    return (float)doubleValue();
  }

  /** Mengimplementasikan metode abstrak doubleValue dalam java.lang.Number */
  public double doubleValue() {
    return pembilang * 1.0 / penyebut;
  }

  /** Mengimplementasikan metode abstrak longValue dalam java.lang.Number */
  public long longValue() {
    return (long)doubleValue();
  }

  /** Mengimplementasikan metode compareTo dalam java.lang.Comparable */
  public int compareTo(Object o) {
    if ((this.subtract((Rasional)o)).dapatPembilang() > 0)
      return 1;
    else if ((this.subtract((Rasional)o)).dapatPembilang() < 0)
      return -1;
    else
      return 0;
  }
}

1 comment: