Package dan Import

Mengorganisasi kode ke dalam package serta memilih antara import dan nama fully-qualified.

Daftar isi

Kelas yang dipakai dalam program berasal dari dua sumber, yaitu kelas yang ditulis sendiri dan kelas bawaan Java seperti String atau Scanner. Java mengelompokkannya ke dalam package, yaitu unit pengelompokan yang sekaligus menjadi ruang nama (namespace). Topik ini membahas cara mendeklarasikan package, cara import memangkas penulisan namanya, dan cara memilih nama fully-qualified (fully-qualified name) ketika dua nama kelas bertabrakan.

Package sebagai ruang nama kelas#

Setiap kelas di Java berada di dalam sebuah package, dan nama lengkap kelasnya terdiri atas nama package ditambah nama kelas, misalnya java.util.Random. Dua kelas boleh bernama sama asalkan package-nya berbeda, sehingga ruang nama ini mencegah tabrakan nama pada proyek yang menggabungkan banyak pustaka. Program berukuran satu berkas biasanya tidak menyatakan package, sehingga kelasnya berada di package tak bernama (unnamed package), yaitu kondisi yang wajar untuk tahap belajar.

Pada proyek yang dikompilasi dengan javac, package dipetakan ke folder. Deklarasi package util; berarti berkasnya berada di dalam folder util, sehingga bentuk fisiknya dan deklarasinya saling mengunci. Struktur berikut adalah proyek kecil dengan dua package.

text
aplikasi-nilai/
└── src/
    ├── util/
    │   └── KonversiNilai.java
    └── laporan/
        └── CetakLaporan.java

Dua berkas pada pohon di atas dideklarasikan seperti berikut. Berkas pertama menyediakan metode pembantu, sedangkan berkas kedua memakainya lewat import.

java Contoh: berkas yang menyatakan package
// tidak-dijalankan
package util;

public class KonversiNilai {
    public static int skala(int nilai, int maks) {
        return nilai * 100 / maks;
    }
}
java Contoh: berkas di package lain yang mengimpor kelasnya
// tidak-dijalankan
package laporan;

import util.KonversiNilai;

public class CetakLaporan {
    public static void main(String[] args) {
        System.out.println(KonversiNilai.skala(80, 100));
    }
}

Konvensi penamaannya konsisten huruf kecil, tanpa garis bawah dan tanpa huruf kapital. Pustaka yang disebarluaskan ke pihak lain lazimnya memakai nama domain terbalik, misalnya com.contoh.perhitungan, agar unik secara global. Package bawaan platform Java diawali java, seperti java.util dan java.lang, dan di antara semuanya java.lang mendapat perlakuan khusus yang dibahas pada bagian berikutnya.

Mengimpor kelas dengan import#

Tanpa import, kelas dari package lain tetap bisa dipakai dengan menulis nama lengkapnya setiap kali dipanggil. Import memperkenalkan nama pendeknya pada kompiler, sehingga cukup ditulis Random di sisa berkas. Deklarasinya ditempatkan di bawah pernyataan package dan di atas deklarasi kelas.

Program berikut memakai kelas Random dari package java.util untuk mengocok dua dadu. Kelas Math dari package java.lang dipakai tanpa import, karena package java.lang diimpor otomatis pada setiap berkas Java.

java DemoImport.java
import java.util.Random;

public class DemoImport {
    public static void main(String[] args) {
        // Random berada di package java.util, sehingga perlu diimpor
        Random acak = new Random(42);

        int daduSatu = acak.nextInt(6) + 1;
        int daduDua = acak.nextInt(6) + 1;

        // Math berada di package java.lang yang diimpor otomatis
        System.out.println("Dadu 1  : " + daduSatu);
        System.out.println("Dadu 2  : " + daduDua);
        System.out.println("Terbesar: " + Math.max(daduSatu, daduDua));
    }
}
text Keluaran
Dadu 1  : 3
Dadu 2  : 4
Terbesar: 4

Pemanggilan new Random(42) mengisi biji (seed) pengacak, sehingga urutan angkanya tetap sama pada setiap eksekusi dan cocok untuk contoh yang harus dapat diulang. Tanpa biji, Random memakai sumber keacakan sistem dan hasilnya berubah setiap kali program dijalankan. Dua bentuk import yang tersedia adalah import java.util.Random; untuk satu kelas dan import java.util.*; untuk seluruh kelas dalam satu package. Berkas yang membutuhkan banyak kelas asing menuliskan baris import berturut-turut di bagian atasnya. Konvensinya mengurutkan import secara alfabetis agar mudah dicari, sehingga dua import dari satu package bersebelahan.

Memilih antara import dan nama fully-qualified#

Pilihan antara keduanya ditentukan oleh kebiasaan berkasnya. Import per kelas menjadi pilihan utama, karena asal setiap nama terbaca pada bagian atas berkas. Nama fully-qualified dipilih ketika pemakaiannya hanya sekali, atau ketika dua kelas senama harus dipakai dalam satu berkas, karena import ganda atas dua kelas bernama sama ditolak kompiler.

SituasiPilihan yang lazim
Kelas dipakai beberapa kali dalam satu berkasimport satu kelas
Banyak kelas dari satu packageimport dengan tanda bintang, misalnya java.util.*
Kelas dipakai satu kali sajanama fully-qualified tanpa import
Dua kelas senama dari package berbedanama fully-qualified untuk salah satu atau keduanya

Contoh berikut memakai dua kelas Tanggal dari dua package berbeda dalam satu berkas. Import untuk keduanya tidak dapat dinyatakan sekaligus, sehingga salah satunya ditulis lengkap.

java Contoh: dua kelas senama dipisahkan lewat nama lengkapnya
// tidak-dijalankan
import laporan.Tanggal;

Tanggal tanggalCetak = new Tanggal();
util.Tanggal tanggalUjian = new util.Tanggal();

Import dengan tanda bintang praktis pada package yang dipakai menyeluruh, tetapi bentuk ini menyembunyikan asal setiap nama. Peluang ambiguitas juga naik bila dua package yang diimpor memuat kelas bernama sama, sehingga kompiler menampilkan pesan reference to Tanggal is ambiguous. Kebiasaan yang aman adalah import eksplisit per kelas, lalu nama fully-qualified untuk kasus khusus. Pada berkas yang panjang, daftar import di bagian atasnya menjadi peta ketergantungan yang mudah dibaca, sehingga asal setiap nama pendek dapat dilacak sekejap.

Mengompilasi proyek bertanda package#

Proyek dengan pernyataan package dikompilasi dari folder akar sumbernya, yaitu folder yang langsung memuat package-package terluar. Perintah javac mengompilasi seluruh berkas sekaligus, sedangkan perintah java menjalankan kelas utama dengan nama fully-qualified-nya, memakai tanda titik sebagai pemisah dan tanpa akhiran .java. Nama berkas sumber diberikan lengkap dengan foldernya kepada javac, sedangkan java tidak menerima berkas sama sekali, melainkan nama kelas yang dicari di dalam classpath.

bash
# tidak-dijalankan
# dijalankan dari dalam folder src, hasil kompilasi dikumpulkan di folder out
javac -d out util/KonversiNilai.java laporan/CetakLaporan.java
# menjalankan kelas utama dengan nama fully-qualified-nya
java -cp out laporan.CetakLaporan

Opsi -d out mengumpulkan hasil kompilasi ke folder out, dan opsi -cp menunjuk folder akar tempat package bermula, sehingga java dapat menemukan laporan.CetakLaporan di dalamnya. Folder out pun mencerminkan struktur package, yaitu out/util/KonversiNilai.class dan out/laporan/CetakLaporan.class, sehingga pohon folder sumber terulang kembali pada hasil kompilasi. Perintah-perintah tersebut tidak dapat dijalankan di lingkungan penulisan topik ini karena javac tidak tersedia di sana, tetapi jalannya sama pada mesin dengan JDK terpasang. Keluaran programnya sama persis dengan versi satu berkas, karena package tidak mengubah perilaku kode, melainkan mengatur alamat dan hubungan antarkelasnya.

Kesalahan umum#

  • Kelas dipakai tanpa import dan tanpa nama lengkap, sehingga kompiler menolak dengan cannot find symbol: class Random. Perbaikannya menambahkan import java.util.Random; atau menulis java.util.Random di tempat pemakaiannya.
  • Berkas tidak diletakkan di folder yang sesuai dengan package-nya, sehingga kelas yang mengimpornya gagal dikompilasi dengan cannot find symbol. Perbaikannya menyamakan letak berkas dengan deklarasinya, misalnya KonversiNilai.java di dalam folder util.
  • Kelas bertanda package dijalankan dengan nama pendek, misalnya java CetakLaporan, dan JVM menjawab Error: Could not find or load main class. Perbaikannya memakai nama fully-qualified, yaitu java -cp out laporan.CetakLaporan.
  • Mengira import menambah ukuran program atau menyalin kode. Import hanya pemberian nama bagi kompiler, dan tidak ada isi package yang disalin ke dalam berkas.
  • Meninggalkan baris import yang tidak terpakai. Program tetap berjalan, tetapi daftar import yang panjang menyesatkan pembaca tentang ketergantungan berkasnya. Hapus import yang tidak dipakai, dan IDE umumnya menyediakan aksinya.

Ringkasan#

  • Package adalah ruang nama yang dipetakan ke folder, dan nama lengkap kelasnya berbentuk namapackage.NamaKelas.
  • Import memperkenalkan nama pendek kelas pada kompiler, package java.lang diimpor otomatis, dan tidak ada kode yang disalin oleh import.
  • Import per kelas menjadi pilihan utama, sedangkan nama fully-qualified menyelesaikan pemakaian sekali pakai dan bentrokan nama.
  • Proyek bertanda package dikompilasi dari folder akar sumber dan dijalankan dengan nama fully-qualified-nya.

Referensi#