Sunday, March 18, 2018

15. Ekspresi Lambda Bagian Tiga



Referensi Metode ke Metode Objek
Untuk melewatkan sebuah referensi yang menunjuk ke metode objek pada suatu objek spesifik, Anda menggunakan sintaksis dasar ini:

refObjek::namaMetode

Seperti yang dapat Anda lihat, sintaksis ini sama dengan yang digunakan untuk metode static, kecuali bahwa di sini sebuah referensi objek dipakai menggantikan nama kelas. Berikut adalah program sebelumnya, yang telah ditulis ulang untuk menggunakan sebuah referensi metode objek:

// Mendemonstrasikan sebuah referensi metode ke metode objek.

// Antarmuka fungsional untuk operasi-operasi string.
interface FungsiString {
   String fungsi(String n);
}

// Kelas ini mendefinisikan sebuah metode objek dengan nama balikString().
class OpStringKu {
   String balikString(String str) {
      String hasil = "";
      int i;

      for(i = str.length()-1; i >= 0; i--)
       hasil += str.charAt(i);

      return hasil;
   }
}

public class DemoReferensiMetode2 {
   // Metode ini memiliki sebuah antarmuka fungsional sebagai
   // tipe data dari parameternya. Jadi, ia dapat
   // menerima sembarang objek dari antarmuka tersebut,
   // termasuk sebuah referensi metode.
   static String stringOp(FungsiString sf, String s) {
      return sf.fungsi(s);
   }
 
   public static void main(String args[])
   {
      String strMasukan = "Lambda menambah kekuatan";
      String strKeluaran;
      
      // Menciptakan sebuah objek OpStringKu.
      OpStringKu strOp = new OpStringKu( );
 
      // Di sini, sebuah ref metode ke balikString
      // dilewatkan kepada stringOp().
      strKeluaran = stringOp(strOp::balikString, strMasukan);
      
      System.out.println("String semula: " + strMasukan);
      System.out.println("String jika dibalik: " + strKeluaran);
   }
}

Program ini menghasilkan keluaran yang sama seperti versi program sebelumnya.

Pada program, perhatikan bahwa balikString() sekarang menjadi metode objek dari kelas OpStringKu. Di dalam main(), sebuah objek dari OpStringKu, yaitu strOp, diciptakan. Objek ini dipakai untuk menciptakan referensi metode yang menunjuk ke balikString() pada pemanggilan stringOp:


strKeluaran = stringOp(strOp::balikString, strMasukan);

Pada contoh ini, balikString() dipanggil pada objek strOp.

Anda dimungkinkan pula untuk menangani situasi dimana Anda ingin menetapkan sebuah metode objek yang dapat dipakai dengan sembarang objek dari kelas tertentu, bukan hanya objek tertentu. Pada kasus ini, Anda akan menciptakan sebuah referensi metode dengan cara berikut:

NamaKelas::namaMetodeObjek

Di sini, nama kelas dipakai menggantikan nama objek spesifik, meskipun metode objek ditetapkan. Dengan bentuk ini, parameter pertama dari antarmuka fungsional harus cocok dengan objek pemanggil dan parameter keduanya harus cocok dengan parameter yang ditetapkan oleh metode.

Berikut diberikan sebuah contoh. Contoh ini mendefinisikan sebuah metode dengan nama pencacah() yang menghitung banyak objek pada sebuah array yang memenuhi kondisi yang didefinisikan oleh metode fungsi() pada antarmuka fungsional FungsiKu. Pada kasus ini, ia menghitung banyak objek dari kelas SuhuTinggi.

// Menggunakan sebuah referensi metode objek dengan objek-objek berbeda.

// Sebuah antarmuka fungsional yang mengambil dua argumen referensi
// dan menghasilkan sebuah hasil boolean.
interface FungsiKu<T> {
   boolean fungsi(T v1, T v2);
}

// Sebuah kelas yang menyimpan suhu tinggi dalam sehari.
class SuhuTinggi {
   private int hTemp;

   SuhuTinggi(int ht) { hTemp = ht; }

   // Menghasilkan true jika objek SuhuTinggi pemanggil memiliki
   // suhu yang sama dengan ht2.
   boolean suhuSama(SuhuTinggi ht2) {
      return hTemp == ht2.hTemp;
   }

   // Menghasilkan true jika objek SuhuTinggi pemanggil memiliki
   // suhu yang lebih rendah dari ht2.
   boolean suhuKurangDari(SuhuTinggi ht2) {
      return hTemp < ht2.hTemp;
   }
}

public class DemoRefObjekDengaMetodeObjek {
   // Sebuah metode yang menghasilkan banyak kemunculan
   // dari sebuah objek dengan kriteria tertentu, seperti
   // ditetapkan oleh parameter FungsiKu, yang bernilai true.
   static <T> int pencacah(T[] arrayNilai, FungsiKu<T> f, T v) {
      int cacah = 0;
  
      for(int i=0; i < arrayNilai.length; i++)
         if(f.fungsi(arrayNilai[i], v)) cacah++;
            return cacah;
   }
  
   public static void main(String args[])
   {
      int cacah;
  
      // Menciptakan sebuah array yang memuat objek-objek SuhuTinggi.
      SuhuTinggi[] suhuTinggiSeminggu = { new SuhuTinggi(89), new SuhuTinggi(82),
         new SuhuTinggi(90), new SuhuTinggi(89),
         new SuhuTinggi(89), new SuhuTinggi(91),
         new SuhuTinggi(84), new SuhuTinggi(83) };
  
      // Menggunakan pencacah(), dengan array-array dari kelas SuhuTinggi.
      // Perhatikan bahwa sebuah referensi ke metode objek
      // suhuSama() dilewatkan sebagai argumen kedua.
      cacah = pencacah(suhuTinggiSeminggu, SuhuTinggi::suhuSama,
         new SuhuTinggi(89));
      System.out.println(cacah + " hari memiliki suhu 89 derajat F");
  
      // Sekarang, ciptakan dan gunakan array lain yang memuat 
      // objek-objek SuhuTinggi.
      SuhuTinggi[] suhuTinggiSeminggu2 = { new SuhuTinggi(32), new SuhuTinggi(12),
         new SuhuTinggi(24), new SuhuTinggi(19),
         new SuhuTinggi(18), new SuhuTinggi(12),
         new SuhuTinggi(-1), new SuhuTinggi(13) };
  
      cacah = pencacah(suhuTinggiSeminggu2, SuhuTinggi::suhuSama,
         new SuhuTinggi(12));
      System.out.println(cacah + " hari memiliki suhu 12 derajat F");
  
      // Sekarang, gunakan suhuKurangDari() untuk mencari hari-hari dimana
      // suhu kurang dari nilai yang ditetapkan.
      cacah = pencacah(suhuTinggiSeminggu, SuhuTinggi::suhuKurangDari,
         new SuhuTinggi(89));
      System.out.println(cacah + " hari memiliki suhu kurang dari 89 F");
  
      cacah = pencacah(suhuTinggiSeminggu2, SuhuTinggi::suhuKurangDari,
         new SuhuTinggi(19));
      System.out.println(cacah + " hari memiliki suhu kurang dari 19 F");
   }
 }

Ketika dijalankan, program ini menghasilkan keluaran:

3 hari memiliki suhu 89 derajat F
2 hari memiliki suhu 12 derajat F
3 hari memiliki suhu kurang dari 89 F
5 hari memiliki suhu kurang dari 19 F

Pada program, perhatikan bahwa SuhuTinggi memiliki dua metode kelas: suhuSama() dan suhuKurangDari(). Metode pertama menghasilkan true jika dua objek SuhuTinggi memiliki suhu sama. Metode kedua menghasilkan true jika suhu dari objek pemanggil bernilai kurang dari objek yang dilewatkan. Setiap metode memiliki sebuah parameter bertipe data SuhuTinggi dan tiap metode menghasilkan hasil boolean. Jadi, setiap metode kompatibel dengan antarmuka fungsional FungsiKu karena tipe data dari objek pemanggil dapat dipetakan menjadi parameter pertama dari fungsi() dan argumen dipetakan ke parameter kedua dari fungsi().

Jadi, ketika ekspresi

SuhuTinggi::suhuSama

dilewatkan kepada metode pencacah(), sebuah objek dari antarmuka fungsional FungsiKu akan diciptakan dimana di dalamnya tipe data dari parameter pertamanya adalah tipe data dari objek pemanggil dari metode objek, yaitu SuhuTinggi. Tipe data dari parameter kedua juga adalah SuhuTinggi karena itu merupakan tipe data dari parameter suhuSama(). Ini berlaku juga untuk metode suhuKurangDari(). Satu hal penting lain: Anda dapat mereferensi versi superkelas dari sebuah metode menggunakan katakunci super, seperti berikut:

super::nama

Nama metode ditetapkan oleh nama.


Referensi Metode dengan Generik
Anda dapat menggunakan referensi metode dengan kelas generik dan/atau metode generik. Sebagai contoh, perhatikan program berikut:

// Mendemonstrasikan sebuah referensi metode ke 
// sebuah metode generik yang dideklarasikan di dalam
// sebuah kelas tak-generik.

// Sebuah antarmuka fungsional yang beroperasi pada
// sebuah array dan sebuah nilai, dan menghasilkan
// nilai balik int.
interface FungsiKu<T> {
   int fungsi(T[] vals, T v);
}

// Kelas ini mendefinisikan sebuah metode dengan nama
// cacahCocok() yang menghasilkan banyak item di dalam
// sebuah array yang sama dengan nilai tertentu. Perhatikan
// bahwa cacahCocok() merupakan metode numerik
// tetapi OpArrayKu bukan generik.
class OpArrayKu {
   static <T> int cacahCocok(T[] arrayNilai, T v) {
      int cacah = 0;

      for(int i=0; i < arrayNilai.length; i++)
         if(arrayNilai[i] == v) cacah++;
  
      return cacah;
   }
}
public class DemoRefMetodeGenerik {
   // Metode ini memiliki antarmuka fungsional FungsiKu
   // sebagai tipe data dari parameter pertamanya. Dua
   // parameter lain menerima sebuah array dan sebuah nilai,
   // keduanya bertipe T.
   static <T> int opKu(FungsiKu<T> f, T[] arrayNilai, T v) {
      return f.fungsi(arrayNilai, v);
   }
 
   public static void main(String args[])
   {
      Integer[] arrayNilai = { 1, 2, 3, 4, 2, 3, 4, 4, 5 };
      String[] arrayString = { "Satu", "Dua", "Tiga", "Dua" };
      int cacah;
 
      cacah = opKu(OpArrayKu::<Integer>cacahCocok, arrayNilai, 4);
      System.out.println("arrayNilai memuat " + cacah + " buah 4");
 
      cacah = opKu(OpArrayKu::<String>cacahCocok, arrayString, "Dua");
      System.out.println("arrayString memuat " + cacah + " buah Dua");
   }
}

Jika dijalankan, program di atas menghasilkan keluaran berikut:

arrayNilai memuat 3 buah 4
arrayString memuat 2 buah Dua

Pada program, OpArrayKu merupakan sebuah kelas tak-generik yang memuat sebuah metode generik dengan nama cacahCocok(). Metode ini menghasilkan banyak elemen array yang cocok dengan nilai tertentu. Perhatikan bagaimana argumer bertipe generik dibuat. Sebagai contoh, pemanggilan pertamanya di dalam main() berikut:

cacah = opKu(OpArrayKu::<Integer>cacahCocok, arrayNilai, 4);

melewatkan argumen bertipe Integer. Perhatikan bahwa hal itu terjadi setelah operator ::. Sintaksis ini dapat digeneralisir. Ketika sebuah metode generik ditetapkan sebagai referensi metode, tipe data argumennya ditempatkan setelah operator :: dan sebelum nama metode.

Salah satu cara mencari elemen terbesar pada sebuah koleksi adalah dengan menggunakan metode max() yang didefinisikan oleh kelas Collections. Untuk versi max() yang digunakan di sini, Anda harus melewatkan sebuah referensi ke koleksi dan sebuah objek yang mengimplementasikan antarmuka Comparator<T>. Antarmuka ini menetapkan bagaimana dua objek dibandingkan. Antarmuka ini juga hanya mendefinisikan satu metode abstrak, dinamakan dengan compare(), yang mengambil dua argumen, yang masing-masing merupakan tipe data objek yang sedang dibandingkan. Metode ini menghasilkan nilai yang lebih besar dari nol jika argumen pertama lebih besar dari argumen kedua, menghasilkan nol jika kedua argumen sama, dan menghasilkan nilai yang lebih kecil dari nol jika objek pertama bernilai kurang dari objek kedua.

Di masa lalu, untuk menggunakan max() pada objek-objek yang didefinisikan sendiri oleh user, sebuah objek dari Comparator<T> harus diperoleh dengan lebih dahulu secara eksplisit mengimplementasikannya menggunakan sebuah kelas, dan kemudian menciptakan objek dari kelas itu. Objek ini kemudian dilewatkan sebagai komparator kepada max(). Dengan JDK 8, Anda dimungkinkan hanya dengan melewatkan sebuah referensi yang menunjuk ke sebuah metode perbandingan kepada max(). Contoh sederhana berikut menunjukkan proses dengan menciptakan objek-objek dari KelasKu dan kemudian mencari satu objek yang memiliki nilai tertinggi (seperti didefinisikan oleh metode perbandingan).


// Menggunakan sebuah referensi metode untuk membantu mencari
// nilai maksimum pada sebuah koleksi.
import java.util.*;

class KelasKu {
   private int nil;

   KelasKu(int v) { nil = v; }
   
   int getNil() { return nil; }
}

public class MenggunakanRefMetode {
   // Sebuah metode perbandingan yang kompatibel dengan
 // yang didefinisikan oleh Comparator<T>.
   static int bandingKelasKu(KelasKu a, KelasKu b) {
      return a.getNil() - b.getNil();
   }
 
   public static void main(String args[])
   {
      ArrayList<KelasKu> al = new ArrayList<KelasKu>();
 
      al.add(new KelasKu(1));
      al.add(new KelasKu(4));
      al.add(new KelasKu(2));
      al.add(new KelasKu(9));
      al.add(new KelasKu(3));
      al.add(new KelasKu(7));
 
      // Mencari nilai maksimum pada al
      // menggunakan metode bandingKelasKu().
      KelasKu objNilaiMaks = Collections.max(al, MenggunakanRefMetode::bandingKelasKu);
 
      System.out.println("Nilai maksimum =: " + objNilaiMaks.getNil());
   }
}

Jika dijalankan, program di atas menghasilkan keluaran berikut:

Nilai maksimum =: 9

Pada program, perhatikan bahwa KelasKu tidak mendefinisikan metode perbandingan apapun dan juga tidak mengimplementasikan Comparator. Namun, nilai maksimum dari sebuah list dengan item-item bertipe data KelasKu masih dapat diperoleh dengan memanggil max() karena MenggunakanRefMetode mendefinisikan metode static bandingKelasKu, yang kompatibel dengan metode campare() yang didefinisikan oleh Comparator. Oleh karena itu, Anda tidak perlu secara eksplisit mengimplementasikan dan menciptakan objek dari Comparator.


Selanjutnya  >>>



No comments: