Mengolah data bertipe campuran di Java klasik selalu berpasangan dua langkah: uji tipe dengan instanceof, lalu cast eksplisit ke variabel baru agar komponennya terbuka. Pola if (x instanceof String) { String s = (String) x; ... } berulang di mana-mana, dan compiler sebenarnya sudah tahu bahwa cast itu pasti berhasil — informasinya hanya tidak dibagikan. Pattern matching menggabungkan pengujian dan pengikatan menjadi satu konstruksi: if (x instanceof String s). Pattern matching untuk instanceof difinalkan di Java 16 (JEP 394), lalu diperluas ke switch di Java 21 melalui JEP 441; keduanya standar di Java 25 dan menjadi fondasi record pattern pada chapter berikutnya.
Type Pattern pada instanceof #
Konstruksi tipe nama disebut type pattern: saat runtime, JVM menguji apakah nilai bertipe yang disebut; bila cocok, variabel diikat otomatis dengan tipe yang sudah tepat — tanpa cast. Cakupan variabelnya mengikuti alur (flow scoping): variabel hanya pasti terikat di cabang yang lolos pengujian, sehingga bisa langsung dipakai di dalam blok if maupun pada kondisi && berikutnya.
if (nilai instanceof String teks && teks.length() > 3) {
System.out.println(teks.toUpperCase()); // teks sudah bertipe String
}Type Pattern pada switch dan Kondisi when #
Pada switch, type pattern menggantikan pemeriksaan berantai if-else instanceof. Klausa when menambahkan syarat pada label yang sama — begitu satu label berpola sama perlu dibedakan lebih lanjut. Contoh teruji berikut menghitung luas bangun datar dari hierarki tertutup:
import java.util.List;
public class PencocokanTipe {
sealed interface BangunDatar permits Lingkaran, Persegi, PersegiPanjang { }
record Lingkaran(double jari) implements BangunDatar { }
record Persegi(double sisi) implements BangunDatar { }
record PersegiPanjang(double lebar, double tinggi) implements BangunDatar { }
// Gaya lama: uji tipe lalu cast manual ke variabel baru.
static String labelLama(Object nilai) {
if (nilai instanceof String) {
String teks = (String) nilai;
return "teks: " + teks.length() + " karakter";
}
return "bukan teks";
}
// Pattern matching: uji tipe sekaligus mengikat variabel teks.
static String labelBaru(Object nilai) {
if (nilai instanceof String teks) {
return "teks: " + teks.length() + " karakter";
}
return "bukan teks";
}
static double luas(BangunDatar b) {
return switch (b) {
case Lingkaran l when l.jari() > 0 -> Math.PI * l.jari() * l.jari();
case Lingkaran l -> 0; // jari() <= 0
case Persegi p -> p.sisi() * p.sisi();
case PersegiPanjang pp -> pp.lebar() * pp.tinggi();
};
}
public static void main(String[] args) {
System.out.println("lama : " + labelLama("Halo"));
System.out.println("baru : " + labelBaru(42));
var bentuk = List.of(
new Lingkaran(7),
new Persegi(9),
new PersegiPanjang(4, 5),
new Lingkaran(-1));
for (BangunDatar b : bentuk) {
System.out.printf("%-32s luas %.2f%n", b, luas(b));
}
}
}Output yang Dihasilkan:
lama : teks: 4 karakter
baru : bukan teks
Lingkaran[jari=7.0] luas 153.94
Persegi[sisi=9.0] luas 81.00
PersegiPanjang[lebar=4.0, tinggi=5.0] luas 20.00
Lingkaran[jari=-1.0] luas 0.00Bedah baris-per-baris untuk bagian pentingnya:
- Method
labelLamadanlabelBaru: keduanya menghasilkan keluaran sama; versi pattern menghapus cast manual dan variabel sementara karenatekslangsung terikat dengan tipeString. - Baris
case Lingkaran l when l.jari() > 0 -> ...: label berpolaLingkarandengan syarat tambahan — lingkaran berjari-jari positif dihitung normal. - Baris
case Lingkaran l -> 0;: label kedua berpola sama menampung sisa kasus (jari() <= 0), terlihat pada baris outputLingkaran[jari=-1.0]yang luasnya nol. - Baris
case PersegiPanjang pp -> pp.lebar() * pp.tinggi();: variabel polappmemanggil accessor tanpa cast; berkat hierarkisealed, tidak perludefault.
Urutan Label dan Dominasi #
Label switch berpola dievaluasi dari atas ke bawah, sehingga label yang lebih umum harus diletakkan setelah yang lebih spesifik. Compiler aktif menolak label yang tak mungkin terjangkau — kita verifikasi langsung error berikut pada JDK 25:
error: this case label is dominated by a preceding case labelError itu muncul ketika case Object x ditulis sebelum case String s: pola Object menyerap seluruh nilai, sehingga pola String di bawahnya tidak akan pernah terpilih. Aturan dominance ini melindungi kita dari label mati, sama seperti unreachable statement pada kode berurutan.
Di balik layar, compiler menerjemahkan pola menjadi pemeriksaan bertingkat yang setara dengan versi instanceof manual — hasil bytecode tidak mengandung mekanisme baru yang rahasia. Bedanya murni pada jaminan: compiler memeriksa urutan, dominasi, dan kelengkapan label sebelum program berjalan, pekerjaan yang pada gaya lama dilakukan mata manusia dengan tingkat kelelahan yang berbeda-beda.
| Konstruksi | Sejak | Fungsi |
|---|---|---|
x instanceof T t | Java 16 | Uji tipe + pengikatan variabel |
case T t -> | Java 21 | Type pattern pada switch |
case T t when syarat -> | Java 21 | Type pattern dengan kondisi tambahan |
case null | Java 21 | Menangani null secara eksplisit |
Type pattern membidik tipe dan mengikat satu variabel; record pattern melangkah lebih jauh dengan membedah komponen di dalamnya langsung pada label — materi chapter Record Patterns.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
PencocokanTipe.java
Unduh berkasimport java.util.List;
public class PencocokanTipe {
sealed interface BangunDatar permits Lingkaran, Persegi, PersegiPanjang { }
record Lingkaran(double jari) implements BangunDatar { }
record Persegi(double sisi) implements BangunDatar { }
record PersegiPanjang(double lebar, double tinggi) implements BangunDatar { }
// Gaya lama: uji tipe lalu cast manual ke variabel baru.
static String labelLama(Object nilai) {
if (nilai instanceof String) {
String teks = (String) nilai;
return "teks: " + teks.length() + " karakter";
}
return "bukan teks";
}
// Pattern matching: uji tipe sekaligus mengikat variabel teks.
static String labelBaru(Object nilai) {
if (nilai instanceof String teks) {
return "teks: " + teks.length() + " karakter";
}
return "bukan teks";
}
static double luas(BangunDatar b) {
return switch (b) {
case Lingkaran l when l.jari() > 0 -> Math.PI * l.jari() * l.jari();
case Lingkaran l -> 0; // jari() <= 0
case Persegi p -> p.sisi() * p.sisi();
case PersegiPanjang pp -> pp.lebar() * pp.tinggi();
};
}
public static void main(String[] args) {
System.out.println("lama : " + labelLama("Halo"));
System.out.println("baru : " + labelBaru(42));
var bentuk = List.of(
new Lingkaran(7),
new Persegi(9),
new PersegiPanjang(4, 5),
new Lingkaran(-1));
for (BangunDatar b : bentuk) {
System.out.printf("%-32s luas %.2f%n", b, luas(b));
}
}
}