Satu type parameter sudah cukup untuk wadah seperti Kotak<T>, tetapi data nyata sering berbentuk gabungan: nama dan umur, produk dan harga, kunci dan nilai. Java mengizinkan sebuah class atau method mendeklarasikan beberapa type parameter sekaligus, dipisahkan koma di dalam satu tanda kurung sudut — pola yang paling terkenal dari Map<K, V> di pustaka standar. Setiap type parameter berdiri independen: K boleh diisi String sementara V diisi Integer, dan compiler javac memeriksa keduanya secara terpisah. Pada Chapter ini kita bangun struktur Pasangan<K, V> dan Triple<A, B, C> sendiri, lalu bertemu dengan pasangan resmi dari pustaka standar: Map.Entry<K, V>.
Class Pasangan dengan Dua Type Parameter #
Deklarasi multi type parameter tidak berbeda dari satu type parameter — hanya daftar yang lebih panjang. Berikut class Pasangan yang menyimpan dua nilai dengan dua tipe berbeda:
public class Pasangan<K, V> {
private final K kiri;
private final V kanan;
public Pasangan(K kiri, V kanan) {
this.kiri = kiri;
this.kanan = kanan;
}
public K getKiri() {
return kiri;
}
public V getKanan() {
return kanan;
}
@Override
public String toString() {
return "(" + kiri + ", " + kanan + ")";
}
}Class di atas memakai K untuk nilai kiri dan V untuk nilai kanan. Keduanya bersifat final, diisi lewat constructor, dan dibaca lewat getter yang masing-masing mengembalikan tipe aslinya — getKiri() mengembalikan K, getKanan() mengembalikan V, tanpa cast di sisi pemanggil.
Pemakaian Pasangan, Triple, dan Map Entry #
Satu definisi Pasangan<K, V> melayani kombinasi tipe berbeda-beda, ditambah class Triple dengan tiga type parameter dan pemakaian Map.Entry dari pustaka standar:
import java.util.LinkedHashMap;
import java.util.Map;
public class AplikasiPasangan {
public static void main(String[] args) {
Pasangan<String, Integer> umurBudi = new Pasangan<>("Budi", 17);
Pasangan<String, Double> beratSiti = new Pasangan<>("Siti", 48.5);
System.out.println("Umur : " + umurBudi.getKiri() + " = " + umurBudi.getKanan());
System.out.println("Berat : " + beratSiti.getKiri() + " = " + beratSiti.getKanan());
int tahunDepan = umurBudi.getKanan() + 3; // Integer, tanpa cast
System.out.println("Umur 3 tahun lagi: " + tahunDepan);
// Tiga type parameter sekaligus
Triple<String, Integer, Boolean> baris = new Triple<>("pensil", 12, true);
System.out.println("Triple: " + baris);
// Map.Entry: pasangan bawaan pustaka standar
Map<String, Integer> stok = new LinkedHashMap<>();
stok.put("pensil", 12);
stok.put("buku", 7);
for (Map.Entry<String, Integer> entri : stok.entrySet()) {
System.out.println(entri.getKey() + " -> " + entri.getValue());
}
}
}
class Triple<A, B, C> {
private final A pertama;
private final B kedua;
private final C ketiga;
Triple(A pertama, B kedua, C ketiga) {
this.pertama = pertama;
this.kedua = kedua;
this.ketiga = ketiga;
}
@Override
public String toString() {
return "(" + pertama + ", " + kedua + ", " + ketiga + ")";
}
}Output yang Dihasilkan:
Umur : Budi = 17
Berat : Siti = 48.5
Umur 3 tahun lagi: 20
Triple: (pensil, 12, true)
pensil -> 12
buku -> 7Mari kita bedah baris-baris pentingnya:
- Baris 6-7 (
new Pasangan<>("Budi", 17)dannew Pasangan<>("Siti", 48.5)): dua objek dari class yang sama dengan kombinasi type argument berbeda —Pasangan<String, Integer>danPasangan<String, Double>. Diamond<>menyimpulkan keduanya dari argument. - Baris 11 (
umurBudi.getKanan() + 3): hasilgetKanan()bertipeIntegerpersis, sehingga operasi aritmetika langsung sah tanpa cast dariObject. - Baris 15-16 (
Triple<String, Integer, Boolean>): jumlah type parameter tidak dibatasi dua — tiga, empat, atau lebih sah secara bahasa. Konvensi hanya menganjurkan memakai huruf berurutan (A,B,C) atau bermakna (K,V). - Baris 22-24 (
Map.Entry<String, Integer>):entrySet()menghasilkanSet<Map.Entry<String, Integer>>— pasangan kunci-nilai resmi dariMap<K, V>. MethodgetKey()dangetValue()bekerja sepertigetKiri()dangetKanan()milikPasangan.
Urutan Type Parameter Adalah Kontrak #
Nama type parameter hanya pengenal; yang memberi makna adalah urutannya. Pasangan<String, Integer> berarti “kiri String, kanan Integer” — dan menukar urutan argument akan ditolak compiler. Verifikasi di JDK 25 memperlihatkan konstruksi new Pasangan<>(17, "Budi") untuk variabel bertipe Pasangan<String, Integer> gagal dengan:
CekTukar.java:3: error: incompatible types: cannot infer type arguments for PasanganX<>
PasanganX<String, Integer> p = new PasanganX<>(17, "Budi");
^
reason: inference variable K has incompatible bounds
equality constraints: String
lower bounds: IntegerPesan tersebut memperlihatkan inferensi bekerja per posisi: argument 17 menuntut K = Integer, sedangkan deklarasi variabel menuntut K = String — konflik yang tidak mungkin didamaikan. Pasangan<String, Integer> dan Pasangan<Integer, String> adalah dua tipe berbeda, bukan tulisan ganda dari tipe yang sama.
Dua type parameter menyelesaikan satu keluarga masalah, tetapi membuka keluarga masalah baru: apakah Pasangan<Integer, String> bisa dianggap subtype dari Pasangan<Number, String>? Jawabannya tidak — generics Java bersifat invariant, dan inilah alasan wildcard ? diciptakan. Chapter Wildcards membedah topik tersebut selangkah demi selangkah.
Referensi #
- Java API: java.util.Map.Entry
- JLS SE25 §4.5.1: Type Arguments of Parameterized Types
- Java API: java.util.Map
- dev.java: Generics
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
AplikasiPasangan.java
Unduh berkasimport java.util.LinkedHashMap;
import java.util.Map;
public class AplikasiPasangan {
public static void main(String[] args) {
Pasangan<String, Integer> umurBudi = new Pasangan<>("Budi", 17);
Pasangan<String, Double> beratSiti = new Pasangan<>("Siti", 48.5);
System.out.println("Umur : " + umurBudi.getKiri() + " = " + umurBudi.getKanan());
System.out.println("Berat : " + beratSiti.getKiri() + " = " + beratSiti.getKanan());
int tahunDepan = umurBudi.getKanan() + 3; // Integer, tanpa cast
System.out.println("Umur 3 tahun lagi: " + tahunDepan);
// Tiga type parameter sekaligus
Triple<String, Integer, Boolean> baris = new Triple<>("pensil", 12, true);
System.out.println("Triple: " + baris);
// Map.Entry: pasangan bawaan pustaka standar
Map<String, Integer> stok = new LinkedHashMap<>();
stok.put("pensil", 12);
stok.put("buku", 7);
for (Map.Entry<String, Integer> entri : stok.entrySet()) {
System.out.println(entri.getKey() + " -> " + entri.getValue());
}
}
}
class Triple<A, B, C> {
private final A pertama;
private final B kedua;
private final C ketiga;
Triple(A pertama, B kedua, C ketiga) {
this.pertama = pertama;
this.kedua = kedua;
this.ketiga = ketiga;
}
@Override
public String toString() {
return "(" + pertama + ", " + kedua + ", " + ketiga + ")";
}
}
Pasangan.java
Unduh berkaspublic class Pasangan<K, V> {
private final K kiri;
private final V kanan;
public Pasangan(K kiri, V kanan) {
this.kiri = kiri;
this.kanan = kanan;
}
public K getKiri() {
return kiri;
}
public V getKanan() {
return kanan;
}
@Override
public String toString() {
return "(" + kiri + ", " + kanan + ")";
}
}