Enkapsulasi

Menutup detail internal dengan keempat access modifier dan menilai peran package-private.

Daftar isi

Enkapsulasi (encapsulation) adalah praktik menyembunyikan detail internal sebuah kelas dan membuka aksesnya hanya melalui pintu yang dirancang sadar. Pintu itu ditentukan oleh access modifier, yaitu kata kunci yang mengatur siapa saja boleh menyentuh kelas, field, dan metode. Topik ini menunjukkan kerusakan yang lahir dari field yang terbuka sepenuhnya, memetakan keempat modifier Java, lalu menimbang kapan sifat tanpa modifier yang disebut package-private justru tepat dipakai.

Masalahnya field terbuka#

Field yang dideklarasikan tanpa modifier dapat diisi dari mana saja di dalam package yang sama, dan program kecil memang terasa lancar dengan gaya ini. Kerusakannya baru tampak ketika nilai yang masuk tidak lolos akal sehat, karena tidak ada satu baris kode pun yang berhak menolaknya. Contoh berikut membiarkan stok buku diisi angka negatif, dan kompilator tidak punya alasan untuk protes.

java UjiTanpaEnkapsulasi.java
public class UjiTanpaEnkapsulasi {

    public static void main(String[] args) {
        Buku buku = new Buku();
        buku.judul = "Panduan Merawat Sepeda";
        buku.stok = -5;
        System.out.println("Judul : " + buku.judul);
        System.out.println("Stok  : " + buku.stok);
    }
}

class Buku {
    String judul;
    int stok;
}
text Keluaran
Judul : Panduan Merawat Sepeda
Stok  : -5

Objek sekarang membawa data yang mustahil di dunia nyata, yaitu stok minus lima, dan seluruh kode yang membaca stok harus siap menerima kemustahilan itu. Penyebabnya bukan nilai yang salah ketik, melainkan struktur yang tidak menyediakan titik kendali. Enkapsulasi menutup akses langsung ke field, lalu menawarkan metode sebagai satu-satunya jalur masuk, sehingga setiap perubahan dapat diperiksa di satu tempat.

Empat access modifier#

Java menyediakan empat tingkat akses yang ditentukan oleh kata kunci private, protected, public, serta ketiadaan kata kunci itu sendiri. Tingkat akses mengatur dari arah mana sebuah anggota boleh diakses, sebagaimana dirangkum tabel berikut.

ModifierKelas yang samaPackage yang samaSubclass di package lainSemua tempat
privateyatidaktidaktidak
tanpa modifier (package-private)yayatidaktidak
protectedyayayatidak
publicyayayaya

Program berikut memuat kelas Catatan dengan tiga field bertingkat akses, yaitu isi yang private, pengingat yang package-private, dan judul yang public. Karena kelas utama dan Catatan berada di package yang sama, judul dan pengingat dapat diakses dari main, sedangkan isi hanya bisa diubah lewat metode milik Catatan sendiri.

java UjiAkses.java
public class UjiAkses {

    public static void main(String[] args) {
        Catatan catatan = new Catatan();
        catatan.judul = "Belanja Mingguan";
        catatan.pengingat = "Cek stok kulkas";
        catatan.ubahIsi("Susu, roti, telur");
        System.out.println("Judul     : " + catatan.judul);
        System.out.println("Pengingat : " + catatan.pengingat);
        System.out.println("Isi       : " + catatan.bacaIsi());
    }
}

class Catatan {
    private String isi;
    String pengingat;
    public String judul;

    void ubahIsi(String isiBaru) {
        isi = isiBaru;
    }

    String bacaIsi() {
        return isi;
    }
}
text Keluaran
Judul     : Belanja Mingguan
Pengingat : Cek stok kulkas
Isi       : Susu, roti, telur

Metode ubahIsi dan bacaIsi hidup di dalam Catatan, sehingga keduanya bebas menyentuh field private milik kelasnya sendiri. Sebaliknya, akses dari luar kelas ditolak langsung oleh kompilator.

java Contoh: akses yang ditolak kompilator
// tidak-dijalankan
catatan.isi = "diakses dari luar kelas";
// error: isi has private access in Catatan

Tingkat protected belum terasa bedanya dengan package-private pada contoh ini karena kedua kelas berada di package yang sama. Perbedaannya baru muncul ketika pewarisan lintas package terlibat, yaitu subclass di package lain boleh mengakses anggota protected milik induknya, sedangkan anggota package-private tidak.

Membungkus data dengan metode#

Setelah field ditutup, pertanyaan berikutnya adalah pintu seperti apa yang dibuka. Menyalin pola lama dengan membuat metode pengambil dan pengisi untuk setiap field hanya memindahkan masalah, karena nilai mustahil tetap bisa masuk lewat pengisi tanpa pemeriksaan. Pintu yang baik menyatakan operasi bermakna, bukan penyimpanan mentah, misalnya ubahIsi untuk mengganti isi catatan atau metode yang menghitung sesuatu dari data internal.

Pada kelas Catatan, metode ubahIsi menjadi satu-satunya jalur masuk bagi field isi. Bila kelak perlu aturan tambahan, misalnya menolak teks kosong atau memotong teks terlalu panjang, aturan itu cukup ditulis di satu metode dan seluruh pemanggil otomatis patuh. Tidak ada kode luar yang perlu diubah, dan inilah nilai ekonomis enkapsulasi yang paling sering dirasakan ketika program berkembang.

Peran package-private#

Package-private sering dianggap sisa yang tidak sengaja terjadi ketika seseorang lupa menulis public, padahal sifat ini punya peran yang jelas di dalam satu package. Kelas bantu, metode utilitas internal, dan penghubung antarkelas yang tidak perlu diketahui pihak luar cocok disimpan di tingkat ini, karena konsumen di luar package tidak dapat mengandalkannya. Batas yang ketat seperti ini memberi kebebasan untuk mengubah atau menghapus anggota internal tanpa mengecewakan siapa pun.

Pola pemakaiannya yang sehat bergerak dari yang tertutup menuju yang terbuka. Mulailah dengan anggota private, buka menjadi package-private hanya ketika kelas lain di package yang sama memang membutuhkan, lalu naikkan ke protected atau public ketika kebutuhan lintas package muncul dan terbukti. Menaikkan tingkat akses di kemudian hari mudah dilakukan, sedangkan menurunkannya jauh lebih menyulitkan karena sudah ada kode yang bergantung pada keterbukaan itu.

Kesalahan umum#

  • Membuat semua anggota public demi kepraktisan. Keadaan objek tidak lagi bisa dijamin, dan detail internal yang seharusnya bebas berubah kini dipegang oleh banyak pihak. Tutup field dengan private, lalu buka operasi yang memang diperlukan.
  • Memberi modifier private atau protected pada kelas paling luar. Kedua modifier itu hanya sah untuk anggota di dalam kelas atau kelas bersarang, sehingga kompilasi langsung gagal. Kelas teratas hanya boleh public atau tanpa modifier.
  • Menganggap protected berarti hanya subclass yang boleh mengakses. Di dalam package yang sama, anggota protected juga terbuka bagi semua kelas, sama seperti package-private. Perbedaan sesungguhnya baru terlihat saat pewarisan lintas package dipakai.
  • Menunggu program besar untuk mulai menyembunyikan field. Titik kendali yang dibangun belakangan menuntut penelusuran seluruh pemanggil, sedangkan menutup field sejak awal tidak memakan biaya tambahan apa pun.

Ringkasan#

  • Enkapsulasi menutup detail internal kelas dan membukanya lewat metode yang diperiksa, sehingga keadaan objek dapat dijaga dari nilai yang mustahil.
  • Akses anggota ditentukan oleh private, package-private, protected, dan public, dari yang paling tertutup menuju yang paling terbuka.
  • Package-private tidak ditulis dengan kata kunci, cocok untuk kelas dan anggota internal satu package, dan menjadi titik tengah yang sering terlupakan.
  • Menaikkan tingkat akses mudah dilakukan belakangan, sedangkan menurunkannya mahal karena pemakai luar sudah bergantung.

Referensi#