Kata kunci final mengunci satu hal yang jelas, yaitu nilai yang sudah diberikan tidak boleh diganti lagi. Penerapannya berlaku pada variabel lokal, parameter, field, dan konstanta tingkat kelas, serta memunculkan pola blank final, yakni field final yang belum diisi saat deklarasi dan baru diisi satu kali di dalam konstruktor. Topik ini menelusuri keempat penggunaan itu dengan contoh yang dapat dijalankan, lalu menegaskan batas paling sering disalahpahami, yaitu final mengunci ikatan referensi, bukan isi objeknya.
final pada variabel lokal#
Variabel lokal yang diberi final wajib diisi dan tidak dapat diisi ulang. Penulisan semacam ini bekerja sebagai kontrak yang terbaca, yaitu pembaca kode tahu nilai itu tetap dari awal sampai akhir, sedangkan kompilator menegakkan janji tersebut dengan menolak setiap penugasan kedua.
public class UjiFinal {
static final int BATAS_MAKSIMAL = 100;
public static void main(String[] args) {
final double tarifPerKg = 9500.0;
double berat = 12;
double total = tarifPerKg * berat;
System.out.println("Batas maksimal berat : " + BATAS_MAKSIMAL + " kg");
System.out.println("Total ongkos : " + total);
cetakKuitansi(total);
}
static void cetakKuitansi(final double total) {
System.out.println("Kuitansi sebesar " + total + " tercetak");
}
}Batas maksimal berat : 100 kg
Total ongkos : 114000.0
Kuitansi sebesar 114000.0 tercetakVariabel tarifPerKg dideklarasikan final karena tarifnya memang tidak boleh berubah selama program berjalan, sedangkan berat dan total dibiarkan bebas karena keduanya memang dihitung. Pembandingnya ada di BATAS_MAKSIMAL, konstanta tingkat kelas dengan static final yang nilainya diisi saat deklarasi dan penamaannya mengikuti kebiasaan huruf kapital. Percobaan mengganti nilai yang sudah dikunci ditolak oleh kompilator.
// tidak-dijalankan
final double tarifPerKg = 9500.0;
tarifPerKg = 12000.0;
// error: cannot assign a value to final variable tarifPerKgfinal pada parameter#
Parameter yang diberi final berperilaku sama dengan variabel lokal, yaitu tidak boleh diisi ulang di dalam tubuh metode. Metode cetakKuitansi(final double total) pada program di atas menunjukkan polanya, dan sepanjang tidak ada penugasan ulang, kode tetap berjalan normal. Manfaatnya paling terasa pada metode panjang, karena pembaca tidak perlu melacak kemungkinan nilai parameter berubah di tengah jalan.
Perlu ditegaskan bahwa final pada parameter tidak menghalangi isi objek untuk berubah. Parameter bertipe Rekening yang final tetap bisa dipanggil setor-nya, dan perubahan saldo tetap terjadi. Yang dikunci adalah variabel parameternya, sehingga parameter tersebut tidak bisa dialihkan untuk menunjuk objek lain. Aturan ini sejajar dengan perilaku penyalinan nilai pada pemanggilan metode, karena parameter hanyalah salinan referensi.
final pada field dan blank final#
Field final wajib diisi tepat satu kali, dan jalur pengisiannya hanya dua. Cara pertama mengisi langsung pada deklarasi, seperti SALDO_MINIMUM pada contoh berikut. Cara kedua mengisi di dalam konstruktor, dan pola field final yang dideklarasikan tanpa nilai awal inilah yang disebut blank final. Pola ini cocok untuk data yang harus diterima dari luar, misalnya nomor rekening yang baru diketahui saat objek dibuat.
public class Rekening {
static final double SALDO_MINIMUM = 0.0;
final String nomor;
private double saldo;
Rekening(String nomor) {
this.nomor = nomor;
}
double getSaldo() {
return saldo;
}
void setor(double jumlah) {
if (jumlah > 0) {
saldo = saldo + jumlah;
}
}
boolean tarik(double jumlah) {
if (jumlah > 0 && saldo - jumlah >= SALDO_MINIMUM) {
saldo = saldo - jumlah;
return true;
}
return false;
}
public static void main(String[] args) {
Rekening rekening = new Rekening("201-04-771");
rekening.setor(500000);
rekening.tarik(150000);
System.out.println("Nomor : " + rekening.nomor);
System.out.println("Saldo : " + rekening.getSaldo());
}
}Nomor : 201-04-771
Saldo : 350000.0Field nomor dideklarasikan final tanpa nilai awal, lalu diisi satu kali di konstruktor lewat this.nomor = nomor. Setelah konstruksi selesai, nomor tidak dapat diganti lagi, sehingga identitas rekening aman dari kesalahan penugasan. Perhatikan bahwa saldo sengaja tidak dibuat final karena saldonya memang dirancang berubah lewat setor dan tarik, dan penguncian yang salah sasaran seperti ini justru membuat kode gagal dikompilasi.
final dan imutabilitas#
Batas final yang paling sering disalahpahami adalah asumsinya terhadap objek. final pada variabel bertipe kelas mengunci referensinya, bukan isi objek yang ditunjuk, sehingga objek yang sama tetap bisa berubah lewat metodenya. Istilah untuk keadaan yang tidak dapat diubah sama sekali adalah imutabel (immutable), dan sifat itu harus dirancang pada kelasnya, misalnya dengan menutup semua field dan tidak menyediakan metode pengubah.
// tidak-dijalankan
final Rekening rekening = new Rekening("201-04-771");
rekening.setor(500000); // sah, isi objek berubah
rekening = new Rekening("201-04-780");
// error: cannot assign a value to final variable rekeningDua baris pertama di atas berjalan tanpa keluhan karena yang berubah hanyalah saldo, sedangkan baris ketiga ditolak karena mencoba mengalihkan referensi final ke objek lain. Untuk pembawa data yang memang harus imutabel, Java menyediakan record yang menghasilkan kelas dengan semua field final dan tanpa metode pengubah, sehingga janji imutabilitasnya dirangkai oleh bahasanya sendiri.
Kesalahan umum#
- Mengisi field
finaldua kali, misalnya di deklarasi dan lagi di konstruktor. Aturannya satu kali saja, sehingga kompilasi gagal. Pilih salah satu jalur pengisian. - Mengharapkan
finalmencegah perubahan isi objek. Referensi terkunci, tetapi metode pengubah tetap bekerja. Tutup metode pengubahnya bila keimutabelan memang dikehendaki. - Membuat field
finaltanpa memastikan semua konstruktor mengisinya. Blank final wajib diisi di setiap konstruktor, dan konstruktor yang melewatkan pengisian membuat kompilasi gagal. - Memberi
finalpada field yang memang dirancang berubah, seperti saldo. Penguncian itu menutup operasi yang sah dan harus dibuka kembali dengan menghapusfinal.
Ringkasan#
finalmengunci pengisian ulang pada variabel lokal, parameter, dan field, dengan pesan error “cannot assign a value to final variable” sebagai penegaknya.- Blank final adalah field
finaltanpa nilai awal yang wajib diisi tepat satu kali di dalam setiap konstruktor. - Konstanta tingkat kelas ditulis
static finaldengan nama berhuruf kapital dan diisi saat deklarasi. finalmengunci ikatan referensi, bukan isi objek, sehingga keimutabelan harus dirancang pada kelasnya sendiri.
Referensi#
- Types, Values, and Variables, JLS 4.12.4 — Oracle
— spesifikasi variabel
finaldan aturan blank final pada Java SE 21. - Understanding Class Members — Oracle
— pembahasan konstanta tingkat kelas dengan
static final.