Nama package bukan sekadar label — nama tersebut ikut tersebar ke seluruh kode pemanggil, berkas bytecode, dokumentasi, hingga koordinat dependensi di Maven Central. Kesalahan penamaan yang baru disadari setahun kemudian menjadi beban karena nama lengkap class tertanam di mana-mana. Karena itu ekosistem Java sejak lama bersepakat memakai satu konvensi: nama package berbasis domain internet yang dibalik. Pada Chapter ini kita akan membedah konvensi tersebut, aturan teknis apa saja yang mengikat segmen nama, dan prefiks mana yang dipesan platform sehingga tidak boleh kita pakai.

Konvensi Domain Terbalik #

Ide dasarnya: setiap penerbit kode di dunia menguasai satu nama domain, misalnya example.com, dan domain itu praktis menjamin keunikan. Konvensi domain terbalik menuliskan komponen domain dari belakang, lalu menambahkan nama proyek atau modul sebagai segmen akhir. Domain example.com menjadi prefiks com.example, sehingga package com.example.perpustakaan terbaca sebagai “proyek perpustakaan milik example.com”. Bila dua perusahaan berbeda kebetulan membuat class bernama sama, package berbeda menjaga keduanya tetap unik.

Beberapa pola yang umum kita jumpai:

Domain / pemilikNama package khasContoh di ekosistem
example.com (perusahaan)com.example.<proyek>com.google.guava
tokobudi.co.id (domain berlevel banyak)id.co.tokobudi.<proyek>id.co.besi
kampus.ac.id (universitas)id.ac.kampus.<proyek>id.ac.ui.cs
proyek open source tanpa domain khususorg.<nama>.<proyek>org.apache.commons

Konvensi ini hanyalah konvensi — compiler tidak memeriksa apakah Anda benar-benar memiliki domainnya. Meski demikian, mengikuti konvensi membuat kode Anda tidak bertabrakan dengan pustaka lain saat semuanya berkumpul di satu classpath, dan pengguna library Anda dapat menebak asal setiap class hanya dari namanya.

Aturan Teknis Setiap Segmen #

Secara teknis, setiap segmen nama package harus merupakan identifier Java yang sah dan sebaiknya ditulis huruf kecil semua. Aturan mainnya:

  • Huruf kecil seluruhnya — com.example.perpustakaan, bukan Com.Example.Perpustakaan. CamelCase diserahkan untuk nama class agar kontras terlihat: com.example.perpustakaan.KatalogBuku.
  • Karakter yang diizinkan: huruf, angka, garis bawah; tidak boleh diawali angka. Tanda hubung - tidak sah karena bukan bagian dari identifier — sumber kebingungan klasik saat domain contoh-api.com ingin dijadikan package.
  • Bukan keyword Java — segmen com.contoh.int ditolak compiler karena int adalah keyword.
  • Sebaiknya pendek dan bermakna — segmen seperti util, core, api, model lazim dipakai sebagai segmen akhir.

Program kecil berikut mempraktikkan dua hal sekaligus: membangun nama package dari domain terbalik, dan memeriksa sah-tidaknya sebuah kandidat nama:

java NamaPaket.java
public class NamaPaket {

    public static void main(String[] args) {
        System.out.println(bangunPaket("example.com", "perpustakaan"));
        System.out.println(bangunPaket("tokobudi.co.id", "kasir"));
        System.out.println(bangunPaket("kampus.ac.id", "nilai"));

        System.out.println();
        System.out.println("cek java.contoh    : " + cek("java.contoh"));
        System.out.println("cek com.example    : " + cek("com.example"));
        System.out.println("cek com.contoh-api : " + cek("com.contoh-api"));
    }

    static String bangunPaket(String domain, String proyek) {
        String[] bagian = domain.toLowerCase().split("\\.");
        StringBuilder nama = new StringBuilder();
        for (int i = bagian.length - 1; i >= 0; i--) {
            nama.append(bagian[i]).append('.');
        }
        return nama + proyek;
    }

    static String cek(String namaPaket) {
        if (namaPaket.startsWith("java.")) {
            return "terlarang, prefiks java dipesan platform";
        }
        for (String segmen : namaPaket.split("\\.")) {
            if (!segmen.matches("[a-z_][a-z0-9_]*")) {
                return "tidak sah, segmen '" + segmen + "' bukan identifier";
            }
        }
        return "sah";
    }
}

Output yang Dihasilkan:

text
com.example.perpustakaan
id.co.tokobudi.kasir
id.ac.kampus.nilai

cek java.contoh    : terlarang, prefiks java dipesan platform
cek com.example    : sah
cek com.contoh-api : tidak sah, segmen 'contoh-api' bukan identifier

Mari kita telaah bagian pentingnya:

  1. Method bangunPaket: memecah domain berdasarkan titik, lalu merangkai ulang dari segmen terakhir ke pertama — tokobudi.co.id menjadi id.co.tokobudi — dan menempelkan nama proyek di ujung.
  2. Baris if (namaPaket.startsWith("java.")): pemeriksaan pertama method cek menjawab aturan spesifikasi: komponen pertama java dipesan untuk platform Java SE, seperti kita bahas di bagian berikut.
  3. Regex [a-z_][a-z0-9_]*: meniru aturan identifier huruf kecil — segmen harus diawali huruf atau garis bawah, dilanjutkan huruf, angka, atau garis bawah; segmen contoh-api gagal karena memuat tanda hubung.

Prefiks yang Dipesan Platform #

Java Language Specification menegaskan bahwa nama package maupun module yang diawali identifier java dipesan untuk platform Java SE. Sistem class loader di JVM menolak mendefinisikan class buatan kita di sana. Perhatikan percobaan berikut yang kita jalankan sungguhan dengan berkas java/contoh/Demo.java berisi package java.contoh;:

bash Kompilasi lolos, JVM menolak
javac -d kelas java/contoh/Demo.java
java -cp kelas java.contoh.Demo
text
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.contoh

Compiler javac dengan senang hati mengompilasi berkas tersebut, tetapi JVM menolak memuat class-nya dengan SecurityException — bukti bahwa larangan ini ditegakkan di runtime, bukan sekadar anjuran sopan santun. Di luar java., prefiks seperti javax., jdk., sun., dan com.sun. juga sebaiknya dihindari karena dipakai oleh implementasi JDK dan API standar tambahan Oracle; memakainya untuk kode sendiri membuka peluang bentrok nama dengan class internal platform.

Nama Package dan Nama Module #

Konvensi yang sama berlaku untuk nama module pada Java Module System: com.example.perpustakaan adalah nama package sekaligus kandidat nama module yang wajar. JLS bahasa spesifikasinya memakai contoh module seperti com.nighthacks.scrabble dan org.openjdk.compiler — domain terbalik dipakai untuk tujuan keunikan yang sama. Saat kita tiba di Chapter Module Descriptors, kita akan melihat nama ini muncul di baris pertama module-info.java.

Satu catatan terakhir: nama package tidak menjanjikan lokasi unduhan di internet — package id.ac.kampus.nilai tidak harus hidup di server kampus.ac.id. Domain hanyalah sumber keunikan yang praktis, bukan alamat jaringan. Dengan nama yang unik dan teratur, kita siap mengatur siapa yang boleh memanggil class kita — tugas access control antarpaket pada Chapter berikutnya.

Referensi #

Berkas Sumber

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

NamaPaket.java

Unduh berkas
java
public class NamaPaket {

    public static void main(String[] args) {
        System.out.println(bangunPaket("example.com", "perpustakaan"));
        System.out.println(bangunPaket("tokobudi.co.id", "kasir"));
        System.out.println(bangunPaket("kampus.ac.id", "nilai"));

        System.out.println();
        System.out.println("cek java.contoh    : " + cek("java.contoh"));
        System.out.println("cek com.example    : " + cek("com.example"));
        System.out.println("cek com.contoh-api : " + cek("com.contoh-api"));
    }

    static String bangunPaket(String domain, String proyek) {
        String[] bagian = domain.toLowerCase().split("\\.");
        StringBuilder nama = new StringBuilder();
        for (int i = bagian.length - 1; i >= 0; i--) {
            nama.append(bagian[i]).append('.');
        }
        return nama + proyek;
    }

    static String cek(String namaPaket) {
        if (namaPaket.startsWith("java.")) {
            return "terlarang, prefiks java dipesan platform";
        }
        for (String segmen : namaPaket.split("\\.")) {
            if (!segmen.matches("[a-z_][a-z0-9_]*")) {
                return "tidak sah, segmen '" + segmen + "' bukan identifier";
            }
        }
        return "sah";
    }
}