Chapter sebelumnya menutup dengan dua method manual: Integer.valueOf(500) untuk membungkus dan .intValue() untuk membuka. Menulis keduanya setiap saat akan melelahkan — dan sejak Java 5, kita tidak perlu lagi. Compiler javac menyisipkan kedua pemanggilan itu secara otomatis lewat mekanisme autoboxing (primitive → wrapper) dan unboxing (wrapper → primitive). Fasilitas ini membuat List<Integer> terasa seperti List<int> dan aritmetika objek terasa seperti aritmetika primitive. Kenyamanan tersebut datang bersama dua jebakan serius: perbandingan == yang hasilnya bergantung pada cache, dan unboxing null yang menghentikan program saat runtime — keduanya kita uji tuntas pada Chapter ini.
Mekanisme di Balik Layar #
Ketika compiler menemui assignment Integer bonus = 50; pada konteks yang menuntut Integer, compiler menuliskan ulang baris tersebut menjadi Integer bonus = Integer.valueOf(50); — inilah autoboxing. Sebaliknya, ketika variabel Integer muncul dalam ekspresi aritmetika seperti bonus + 10, compiler menyisipkan bonus.intValue() lebih dulu — inilah unboxing. Bytecode hasil kompilasi memuat pemanggilan method tersebut secara eksplisit; kenyamanan hanya terjadi di level kode sumber.
Konversi otomatis ini berlaku di banyak konteks: assignment, argumen method, nilai kembalian, operand operasi aritmetika dan perbandingan, hingga elemen collection.
Autoboxing pada Collection #
Panggung utama autoboxing adalah collection generik. Program berikut menyimpan nilai int ke List<Integer> dan mengambilnya kembali tanpa satu pun pemanggilan valueOf/intValue yang kita tulis manual:
import java.util.ArrayList;
import java.util.List;
public class AutoboxingUnboxing {
public static void main(String[] args) {
List<Integer> skor = new ArrayList<>();
skor.add(80); // autoboxing: int -> Integer
skor.add(90);
skor.add(75);
System.out.println("skor = " + skor);
int pertama = skor.get(0); // unboxing: Integer -> int
System.out.println("pertama = " + pertama);
Integer jumlahBoxed = skor.get(1) + skor.get(2); // unboxing, jumlah, boxing lagi
System.out.println("jumlah = " + jumlahBoxed);
Integer bonus = 50;
int total = bonus + 10; // unboxing bonus dalam ekspresi
System.out.println("total = " + total);
}
}Output yang Dihasilkan:
skor = [80, 90, 75]
pertama = 80
jumlah = 165
total = 60Mari kita bedah baris-baris kuncinya:
- Baris 6-9 (
skor.add(80)): methodaddpadaList<Integer>menerimaInteger, sedangkan80bertipeint. Compiler menyisipkanInteger.valueOf(80)— tiga elemen tersimpan sebagai objek di heap. - Baris 11 (
int pertama = skor.get(0);): arah sebaliknya —getmengembalikanInteger, variabel tujuan bertipeint, compiler menyisipkan.intValue(). - Baris 14 (
skor.get(1) + skor.get(2)): kombinasi dua arah. OperandIntegerdi-unboxing menjadiint, dijumlahkan sebagaiint(90 + 75 = 165), lalu hasilnya di-autoboxing kembali karena variabel penampung bertipeInteger. - Baris 17-18 (
Integer bonus = 50;lalubonus + 10): pembungkusan terjadi di baris pertama dan pembukaan di baris kedua — pasangan yang persis menggambarkan nama chapter ini.
Jebakan == dan Cache Integer #
Berikut eksperimen paling instruktif di seluruh Section ini. Empat baris perbandingan, dua di antaranya membingungkan:
public class JebakanPerbandingan {
public static void main(String[] args) {
Integer seratusA = 100;
Integer seratusB = 100;
System.out.println("100 == 100 : " + (seratusA == seratusB));
Integer duaRatusA = 200;
Integer duaRatusB = 200;
System.out.println("200 == 200 : " + (duaRatusA == duaRatusB));
System.out.println("200 equals 200 : " + (duaRatusA.equals(duaRatusB)));
int duaRatusPrimitif = 200;
System.out.println("200 == int 200 : " + (duaRatusA == duaRatusPrimitif));
}
}Output yang Dihasilkan:
100 == 100 : true
200 == 200 : false
200 equals 200 : true
200 == int 200 : truePenjelasan masing-masing baris:
- Baris 4-5 (
100 == 100→true): kedua variabel di-autoboxing lewatInteger.valueOf(100). SpesifikasiInteger.valueOfmengizinkan cache untuk nilai -128 hingga 127, dan implementasi JDK memakai cache tersebut — kedua pemanggilan mengembalikan objek yang sama, sehingga perbandingan alamat==berhasil. - Baris 8-9 (
200 == 200→false): nilai200berada di luar rentang cache, sehingga setiap autoboxing membuat objek baru di heap. Dua objek berbeda berarti dua alamat berbeda —==gagal meskipun isi nilainya sama persis. - Baris 11 (
equals→true): methodequals()membandingkan isi objek, bukan alamat. Inilah satu-satunya cara pembandingan wrapper yang benar. - Baris 13-14 (
200 == int 200→true): ketika salah satu operand bertipe primitive, compiler melakukan unboxing pada operandIntegersehingga perbandingan berlangsung antar nilai primitive — hasilnya benar secara kebetulan struktur, tetapi peraturan yang bekerja berbeda dari baris 8.
Unboxing null Melempar NullPointerException #
Wrapper boleh bernilai null; primitive tidak. Ketika compiler menyisipkan .intValue() pada variabel yang ternyata null, JVM melempar NullPointerException — dan stack trace-nya kini menyebut method yang disisipkan compiler:
public class BukaBungkusNull {
public static void main(String[] args) {
Integer stok = null;
int nilai = stok;
System.out.println(nilai);
}
}Program berhenti dengan java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "<local1>" is null, menunjuk ke baris int nilai = stok;. Skenario nyatanya menyakitkan: field Integer dari database yang bernilai NULL, lalu tersangkut dalam ekspresi aritmetika — misalnya total += fieldStok. Pengecekan null sebelum unboxing adalah satu-satunya pencegahan.
Autoboxing menuntaskan rangkaian topik tipe data: primitive, reference, konversi, promosi, luapan, presisi, wrapper, hingga pembungkusan otomatis. Satu dimensi terakhir belum kita bahas secara sistematis: di mana sebuah variabel dikenal dan berapa lama variabel tersebut hidup — wilayah scope dan lifetime yang menjadi pokok Chapter penutup Section ini.
Referensi #
- JLS SE25 §5.1.7: Boxing Conversion
- Java API: Integer.valueOf — kebijakan cache
- Oracle: The Numbers Classes
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
AutoboxingUnboxing.java
Unduh berkasimport java.util.ArrayList;
import java.util.List;
public class AutoboxingUnboxing {
public static void main(String[] args) {
List<Integer> skor = new ArrayList<>();
skor.add(80); // autoboxing: int -> Integer
skor.add(90);
skor.add(75);
System.out.println("skor = " + skor);
int pertama = skor.get(0); // unboxing: Integer -> int
System.out.println("pertama = " + pertama);
Integer jumlahBoxed = skor.get(1) + skor.get(2); // unboxing, jumlah, boxing lagi
System.out.println("jumlah = " + jumlahBoxed);
Integer bonus = 50;
int total = bonus + 10; // unboxing bonus dalam ekspresi
System.out.println("total = " + total);
}
}
BukaBungkusNull.java
Unduh berkaspublic class BukaBungkusNull {
public static void main(String[] args) {
Integer stok = null;
int nilai = stok;
System.out.println(nilai);
}
}
JebakanPerbandingan.java
Unduh berkaspublic class JebakanPerbandingan {
public static void main(String[] args) {
Integer seratusA = 100;
Integer seratusB = 100;
System.out.println("100 == 100 : " + (seratusA == seratusB));
Integer duaRatusA = 200;
Integer duaRatusB = 200;
System.out.println("200 == 200 : " + (duaRatusA == duaRatusB));
System.out.println("200 equals 200 : " + (duaRatusA.equals(duaRatusB)));
int duaRatusPrimitif = 200;
System.out.println("200 == int 200 : " + (duaRatusA == duaRatusPrimitif));
}
}