Nilai int kadang perlu masuk ke variabel long, atau angka long perlu dikirim ke method yang menerima double. Untuk arah dari tipe kecil menuju tipe besar, Java melakukan konversi secara otomatis — tanpa perintah tambahan dari kita. Fasilitas ini bernama widening primitive conversion dan aturannya dirumuskan presisi di JLS §5.1.2. Pada Chapter ini kita petakan seluruh jalur yang diizinkan, mengujinya lewat program nyata, dan membongkar satu fakta yang mengejutkan: konversi yang “memperlebar” ternyata tetap bisa menyusutkan ketelitian angka.

Konversi Widening yang Otomatis #

Spesifikasi Java mendefinisikan 19 jalur widening antar tipe primitive. Seluruhnya dapat dirangkum dalam satu rantai beserta satu cabang khusus char:

text
byte --> short --> int --> long --> float --> double
                   ^
                   |
                  char

Konversi widening berlaku otomatis pada tiga konteks utama: assignment (double x = nilaiInt;), method invocation (argumen int dikirim ke parameter double), dan arithmetik (dibahas tuntas pada Chapter Numeric Promotion). Alasannya sederhana: tipe tujuan selalu “lebih besar” atau sama luas dengan tipe asal, sehingga nilai urutan besarnya tidak berubah — 42 tetap bernilai 42 setelah menjadi long, hanya format penyimpanannya yang berubah.

Rantai Konversi dari byte sampai double #

Program berikut mengalirkan satu nilai melalui seluruh rantai widening, menambahkan jalur char → int, lalu menguji konteks method invocation:

public class KonversiOtomatis {
    public static void main(String[] args) {
        byte kecil = 42;
        short sedang = kecil;      // byte -> short
        int bulat = sedang;        // short -> int
        long lebar = bulat;        // int -> long
        float desimal = lebar;     // long -> float
        double presisi = desimal;  // float -> double

        System.out.println("byte   = " + kecil);
        System.out.println("short  = " + sedang);
        System.out.println("int    = " + bulat);
        System.out.println("long   = " + lebar);
        System.out.println("float  = " + desimal);
        System.out.println("double = " + presisi);

        char huruf = 'A';
        int kode = huruf;          // char -> int (kode Unicode)
        System.out.println("char '" + huruf + "' -> int " + kode);

        cetakKuadrat(7);           // int dikirim ke parameter double
    }

    static void cetakKuadrat(double x) {
        System.out.println("kuadrat dari " + x + " = " + (x * x));
    }
}

Output yang Dihasilkan:

text
byte   = 42
short  = 42
int    = 42
long   = 42
float  = 42.0
double = 42.0
char 'A' -> int 65
kuadrat dari 7.0 = 49.0

Mari kita bedah baris-baris kuncinya:

  1. Baris 4-9 (rantai enam assignment): nilai 42 mengalir dari byte hingga double tanpa satu pun cast. Setiap baris adalah konteks assignment; compiler menemukan jalur widening yang sah lalu menyisipkan konversi secara otomatis.
  2. Baris 18-19 (int kode = huruf;): char melebar ke int dengan membaca kode Unicode karakternya — huruf 'A' menjadi 65. Inilah fondasi aritmetika karakter: huruf + 1 menghasilkan int bernilai 66.
  3. Baris 21 (cetakKuadrat(7);): argumen literal 7 bertipe int, sedangkan parameter cetakKuadrat bertipe double. Compiler otomatis melebarkan 7 menjadi 7.0 sebelum method dijalankan — bukti widening bekerja juga di konteks pemanggilan method.
  4. Baris 26 (x * x): hasil 49.0 bukan 49 — sejak masuk parameter double, seluruh perhitungan di dalam method berlangsung di ranah floating-point.

Presisi yang Tersamar #

“Melebar” tidak selalu berarti “tanpa kehilangan”. Tiga jalur widening menyimpan jebakan: int → float, long → float, dan long → double. Meski sama-sama menempati 4 byte, kapasitas significand float (24 bit) lebih sempit daripada int (32 bit) — float hanya menyimpan sekitar 7 digit desimal signifikan, sementara int menyimpan hingga 9-10 digit. Konversinya tetap diizinkan karena urutan besarnya nilai terjaga, tetapi digit-digit terakhir dapat terpotong oleh pembulatan.

Program berikut membuktikannya dengan dua nilai besar:

public class PresisiMelebar {
    public static void main(String[] args) {
        int penduduk = 123456789;
        float kasar = penduduk;    // int -> float, otomatis
        System.out.println("int   : " + penduduk);
        System.out.println("float : " + kasar);

        long butir = 9007199254740993L;
        double kasarL = butir;     // long -> double, otomatis
        System.out.println("long  : " + butir);
        System.out.println("double: " + kasarL);
    }
}

Output yang Dihasilkan:

text
int   : 123456789
float : 1.2345679E8
long  : 9007199254740993
double: 9.007199254740992E15

Dua pengamatan penting dari keluaran di atas:

  1. Baris 5-6 (float kasar = penduduk;): 123456789 berubah menjadi 1.2345679E8 — tiga digit terakhir (789) terpotong oleh pembulatan biner float. Nilainya kira-kira benar, tetapi tidak lagi identik.
  2. Baris 10-11 (double kasarL = butir;): 9007199254740993 menjadi 9.007199254740992E15 — selisih tepat satu satuan hilang diam-diam, karena double hanya menyimpan sekitar 15-16 digit desimal signifikan.

Konversi yang Tidak Diberlakukan #

Widening tidak menutup semua kombinasi. Arah sebaliknya — dari tipe besar ke tipe kecil — bernama narrowing dan tidak pernah dilakukan otomatis. Assignment byte b = nilaiInt; ditolak compiler dengan pesan incompatible types: possible lossy conversion from int to byte; kita wajib menuliskan cast eksplisit seperti (byte) nilaiInt.

Bahkan antar tipe yang sama-sama kecil, jalur tertutup tetap ada. Potongan berikut gagal dikompilasi meski char terlihat “lebih besar” dari byte:

java byte ke char: butuh cast eksplisit
byte b = 65;
char c = b;   // error: incompatible types: possible lossy conversion from byte to char

Compiler menolak karena byte bertanda (jangkauan -128..127) sedangkan char tak bertanda (0..65535); Java tidak mau menebak bagaimana angka negatif seharusnya dipetakan. Penulisan cast eksplisit (char) b menyatakan bahwa kita sendiri yang menanggung risiko konversi tersebut.

Konversi otomatis menuntaskan perjalanan dari tipe kecil ke tipe besar. Namun program nyata juga butuh arah sebaliknya: memampatkan int ke byte, memotong double menjadi int, atau membaca bagian rendah sebuah long. Operasi tersebut bernama type casting — eksplisit, berisiko, dan menjadi fokus Chapter berikutnya.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

KonversiOtomatis.java

Unduh berkas
java
public class KonversiOtomatis {
    public static void main(String[] args) {
        byte kecil = 42;
        short sedang = kecil;      // byte -> short
        int bulat = sedang;        // short -> int
        long lebar = bulat;        // int -> long
        float desimal = lebar;     // long -> float
        double presisi = desimal;  // float -> double

        System.out.println("byte   = " + kecil);
        System.out.println("short  = " + sedang);
        System.out.println("int    = " + bulat);
        System.out.println("long   = " + lebar);
        System.out.println("float  = " + desimal);
        System.out.println("double = " + presisi);

        char huruf = 'A';
        int kode = huruf;          // char -> int (kode Unicode)
        System.out.println("char '" + huruf + "' -> int " + kode);

        cetakKuadrat(7);           // int dikirim ke parameter double
    }

    static void cetakKuadrat(double x) {
        System.out.println("kuadrat dari " + x + " = " + (x * x));
    }
}

PresisiMelebar.java

Unduh berkas
java
public class PresisiMelebar {
    public static void main(String[] args) {
        int penduduk = 123456789;
        float kasar = penduduk;    // int -> float, otomatis
        System.out.println("int   : " + penduduk);
        System.out.println("float : " + kasar);

        long butir = 9007199254740993L;
        double kasarL = butir;     // long -> double, otomatis
        System.out.println("long  : " + butir);
        System.out.println("double: " + kasarL);
    }
}