Package tidak berhenti sebagai ruang nama — package juga adalah batas wilayah untuk access modifier. Level akses package-private, yang tidak memakai kata kunci apa pun, hanya mengizinkan pemanggil dari package yang sama; level public membuka pintu ke seluruh dunia. Kombinasi keduanya memungkinkan sebuah library menyembunyikan mesin internalnya dan hanya memajang API yang stabil. Pada Chapter ini kita akan memetakan empat level akses, membuktikan batas package lewat contoh yang berjalan, lalu melihat bagaimana pilihan modifier membentuk API publik versus bagian internal.
Empat Level Akses #
Java mengenal empat tingkat akses untuk class, interface, field, method, dan constructor. Tabel berikut merangkum siapa saja yang boleh memanggil sebuah anggota, dengan kolom subclass berarti class turunan pada package berbeda:
| Modifier | Class sendiri | Package sama | Subclass (package beda) | Package lain |
|---|---|---|---|---|
private | ya | tidak | tidak | tidak |
| (tanpa modifier / package-private) | ya | ya | tidak | tidak |
protected | ya | ya | ya | tidak |
public | ya | ya | ya | ya |
Dua hal patut digarisbawahi. Pertama, package-private — ditulis tanpa modifier — adalah default bahasa Java; protected justru lebih longgar karena tetap terbuka bagi seluruh class dalam package yang sama. Kedua, protected hanya relevan untuk anggota class; untuk class level atas, pilihannya hanya public atau package-private. Sebuah class utilitas tanpa modifier public hanya bisa dipakai tetangga sepackage dan sama sekali tak terlihat dari luar.
Package-Private sebagai Default yang Disengaja #
Kedengarannya aneh bahwa Java memilih level paling “setengah terbuka” sebagai default. Rasionya: kolaborasi antar class paling sering terjadi dalam satu package — class Kasir dan Buku di com.perpus.inti saling bekerja erat — sementara kode di luar package selayaknya hanya menyentuh apa yang sengaja dijanjikan. Default package-private membuat aturan itu terjadi tanpa kita mengetik apa pun; memajang sesuatu secara public menjadi keputusan eksplisit, bukan kebetulan.
Pola desain yang mengikuti logika ini: mulailah segalanya package-private, lalu naikkan ke public hanya saat bagian lain benar-benar membutuhkannya. Anggota yang terlalu terbuka sulit ditarik kembali karena pemanggil luar sudah bergantung padanya; anggota yang tertutup selalu bisa dibuka nanti dengan aman.
Membuktikan Batas dalam Satu Package #
Mari kita buktikan sendiri aturannya dengan dua class selevel dalam satu direktori — keduanya berada di default package sehingga dihitung satu package. Class Buku memakai tiga modifier sekaligus pada tiga field-nya:
public class Buku {
public String judul;
String kodeInternal;
private double hargaBeli;
public Buku(String judul, String kodeInternal, double hargaBeli) {
this.judul = judul;
this.kodeInternal = kodeInternal;
this.hargaBeli = hargaBeli;
}
public double hargaJual() {
return hargaBeli * 1.3;
}
String labelInternal() {
return kodeInternal + " (internal)";
}
}Class KasirPerpus pada berkas selevel memanggil field public, field package-private, dan method package-private — semuanya sah karena satu package:
public class KasirPerpus {
public static void main(String[] args) {
Buku buku = new Buku("Belajar Java", "BK-001", 80000.0);
System.out.println("judul : " + buku.judul);
System.out.println("kodeInternal : " + buku.kodeInternal);
System.out.println("label : " + buku.labelInternal());
System.out.printf("harga jual : Rp%.2f%n", buku.hargaJual());
}
}Output yang Dihasilkan:
judul : Belajar Java
kodeInternal : BK-001
label : BK-001 (internal)
harga jual : Rp104000.00Bedah singkatnya: baris buku.kodeInternal dan buku.labelInternal() lolos kompilasi hanya karena KasirPerpus satu package dengan Buku; pindahkan KasirPerpus ke package lain dan dua baris itu ditolak compiler. Sebaliknya hargaBeli tetap terkunci bahkan untuk tetangga sepackage — seperti dibuktikan berkas berikut yang gagal dikompilasi:
public class AksesPrivat {
public static void main(String[] args) {
Buku buku = new Buku("Belajar Java", "BK-001", 80000.0);
System.out.println("judul : " + buku.judul);
System.out.println("harga beli : " + buku.hargaBeli);
}
}Compiler menolak dengan pesan yang kita verifikasi langsung: error: hargaBeli has private access in Buku. Hanya method dalam class Buku sendiri — seperti hargaJual() yang membaca hargaBeli — yang boleh menyentuh field private tersebut.
Akses Lintas Package #
Sekarang kita pindahkan pemanggil ke package berbeda dan lihat compiler bekerja. Struktur berikut menaruh Buku di com.perpus.inti dan aplikasi di com.perpus.ui:
src/
├── com/
│ └── perpus/
│ ├── inti/
│ │ └── Buku.java (judul public, kodeInternal package-private)
│ └── ui/
│ └── AplikasiPerpus.javapackage com.perpus.inti;
public class Buku {
String kodeInternal;
public String judul;
public Buku(String judul, String kodeInternal) {
this.judul = judul;
this.kodeInternal = kodeInternal;
}
}Versi AplikasiPerpus yang mencoba membaca kodeInternal gagal dikompilasi dengan pesan persis berikut:
src/com/perpus/ui/AplikasiPerpus.java:9: error: kodeInternal is not public in Buku;
cannot be accessed from outside packageBegitu baris tersebut diganti System.out.println(buku.judul); — field yang memang public — kompilasi berhasil dan program mencetak Belajar Java. Batas package bekerja persis seperti tabel level akses menjanjikannya.
API Publik versus Bagian Internal #
Pembedaan ini adalah inti desain library yang sehat. API publik adalah kontrak yang Anda janjikan kepada pengguna: nama method, parameter, perilaku — semua public, semua terdokumentasi, semua sebaiknya stabil antar versi. Bagian internal adalah segala hal di baliknya: class pembantu, cache, representasi data sementara — dipaketkan package-private agar hanya dikenal rekan sepackage. Saat internal perlu dirombak, tidak ada pemanggil luar yang putus karena mereka memang tidak pernah melihatnya.
Dengan empat level akses dan batas package di tangan, kita memiliki alat menyusun perpustakaan kode yang rapi. Berikutnya kita akan melihat satu variasi import yang membuat kode matematis lebih ringkas tanpa menambah satu pun level akses baru: static import.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
AksesPrivat.java
Unduh berkaspublic class AksesPrivat {
public static void main(String[] args) {
Buku buku = new Buku("Belajar Java", "BK-001", 80000.0);
System.out.println("judul : " + buku.judul);
System.out.println("harga beli : " + buku.hargaBeli);
}
}
Buku.java
Unduh berkaspublic class Buku {
public String judul;
String kodeInternal;
private double hargaBeli;
public Buku(String judul, String kodeInternal, double hargaBeli) {
this.judul = judul;
this.kodeInternal = kodeInternal;
this.hargaBeli = hargaBeli;
}
public double hargaJual() {
return hargaBeli * 1.3;
}
String labelInternal() {
return kodeInternal + " (internal)";
}
}
KasirPerpus.java
Unduh berkaspublic class KasirPerpus {
public static void main(String[] args) {
Buku buku = new Buku("Belajar Java", "BK-001", 80000.0);
System.out.println("judul : " + buku.judul);
System.out.println("kodeInternal : " + buku.kodeInternal);
System.out.println("label : " + buku.labelInternal());
System.out.printf("harga jual : Rp%.2f%n", buku.hargaJual());
}
}