Komentar #
Komentar adalah salah satu hal dalam pemrograman yang sering disepelekan oleh pemula dan disalahgunakan oleh yang lebih berpengalaman. Komentar yang baik bukan sekadar terjemahan kode ke bahasa manusia — ia menjelaskan mengapa sesuatu dilakukan, bukan apa yang dilakukan (itu sudah terlihat dari kode itu sendiri). Kotlin mendukung tiga jenis komentar: komentar satu baris, komentar multi-baris, dan komentar dokumentasi KDoc. Masing-masing punya konteks penggunaan yang tepat, dan memahami kapan menggunakan mana adalah keterampilan yang membedakan kode yang mudah dipelihara dari yang menjadi beban tim.
Komentar Satu Baris #
Komentar satu baris dimulai dengan //. Semua teks setelah // hingga akhir baris diabaikan sepenuhnya oleh compiler — tidak mempengaruhi eksekusi program sama sekali.
// Ini adalah komentar satu baris yang berdiri sendiri
val batasUmur = 18 // batas minimal untuk membuat akun
val harga = 50_000
val diskon = 0.10
val hargaAkhir = harga * (1 - diskon) // hasil: 45000.0
Komentar satu baris paling sering digunakan dalam dua posisi: di baris sendiri sebelum blok kode yang perlu penjelasan, atau di akhir baris sebagai keterangan singkat (inline comment).
Kapan Komentar Satu Baris Tepat Digunakan #
Gunakan komentar satu baris untuk menjelaskan hal-hal yang tidak langsung terlihat dari kode:
// BENAR: menjelaskan alasan di balik magic number
val TIMEOUT_MS = 5_000 // batas toleransi latensi jaringan internal per SLA
// BENAR: menjelaskan edge case yang tidak intuitif
val indeksAkhir = daftar.size - 1 // size() mengembalikan 1-based, indeks adalah 0-based
// BENAR: menandai bagian yang perlu perhatian khusus
val hash = md5(password) // TODO: ganti ke bcrypt sebelum production — lihat tiket SEC-204
// ANTI-PATTERN: komentar yang hanya mengulang kode
val nama = "Budi" // set nama ke Budi
val umur = 25 // set umur ke 25
println(nama) // cetak nama
Komentar terakhir di atas tidak menambah informasi apa pun. Siapa pun yang membaca val nama = "Budi" sudah tahu apa yang sedang terjadi — tidak perlu ditulis ulang dalam bahasa manusia.
Komentar Multi-Baris #
Komentar multi-baris dibuka dengan /* dan ditutup dengan */. Semua teks di antara keduanya — berapa pun banyak barisnya — diperlakukan sebagai komentar.
/*
Algoritma ini menggunakan pendekatan sliding window untuk menghitung
rata-rata bergerak dengan kompleksitas O(n), alih-alih O(n²) jika
menggunakan nested loop. Referensi: https://en.wikipedia.org/wiki/Sliding_window_protocol
*/
fun rataRataBergerak(data: List<Double>, window: Int): List<Double> {
if (data.size < window) return emptyList()
val hasil = mutableListOf<Double>()
var jumlah = data.take(window).sum()
hasil.add(jumlah / window)
for (i in window until data.size) {
jumlah += data[i] - data[i - window]
hasil.add(jumlah / window)
}
return hasil
}
Komentar Multi-Baris Tersarang #
Salah satu keunggulan Kotlin dibanding Java: komentar multi-baris bisa tersarang (nested). Di Java, menempatkan /* ... */ di dalam /* ... */ adalah error kompilasi. Di Kotlin, ini valid.
/*
Fungsi ini menangani tiga skenario berbeda:
/* Skenario 1: data kosong */
Jika list kosong, kembalikan emptyList() langsung
/* Skenario 2: window lebih besar dari data */
Tidak bisa menghitung rata-rata — kembalikan emptyList()
/* Skenario 3: normal */
Hitung dengan sliding window
*/
fun prosesData(data: List<Double>, window: Int): List<Double> {
// implementasi...
return emptyList()
}
Ini berguna terutama saat kamu perlu men-comment-out blok kode yang di dalamnya sudah mengandung komentar multi-baris.
Menonaktifkan Kode Sementara #
Salah satu penggunaan komentar multi-baris yang sangat praktis adalah menonaktifkan blok kode sementara selama debugging atau eksperimen, tanpa menghapusnya.
fun hitungTotal(items: List<Int>): Int {
/*
// Implementasi lama — terlalu lambat untuk dataset besar
var total = 0
for (item in items) {
total += item
}
return total
*/
// Implementasi baru dengan reduce
return items.reduce { acc, item -> acc + item }
}
Jangan biarkan kode yang di-comment-out dalam waktu lama di codebase production. Jika kode lama masih relevan sebagai referensi, simpan di version control (Git history) — bukan sebagai komentar. Kode yang di-comment-out menambah noise dan membingungkan developer lain.
Komentar Dokumentasi — KDoc #
KDoc adalah sistem dokumentasi resmi Kotlin, setara dengan Javadoc di Java. Komentar KDoc dimulai dengan /** dan diakhiri dengan */. Setiap baris di dalamnya biasanya diawali dengan * (spasi, asterisk, spasi) meskipun ini bukan keharusan.
KDoc bukan sekadar komentar biasa — ia dibaca oleh tool seperti Dokka untuk menghasilkan dokumentasi HTML otomatis, dan ditampilkan langsung di IntelliJ IDEA saat kamu hover ke atas fungsi atau kelas.
/**
* Menghitung bunga majemuk berdasarkan modal awal, suku bunga, dan durasi.
*
* Formula yang digunakan: A = P(1 + r/n)^(nt)
* di mana P adalah modal awal, r adalah suku bunga tahunan (desimal),
* n adalah frekuensi penggandaan per tahun, dan t adalah durasi dalam tahun.
*
* @param modal Modal awal dalam Rupiah
* @param sukuBunga Suku bunga tahunan dalam bentuk desimal (contoh: 0.05 untuk 5%)
* @param tahun Durasi investasi dalam tahun
* @param frekuensi Frekuensi penggandaan per tahun (default: 12 untuk bulanan)
* @return Total nilai investasi setelah periode yang ditentukan
*/
fun bungaMajemuk(
modal: Double,
sukuBunga: Double,
tahun: Int,
frekuensi: Int = 12
): Double {
return modal * Math.pow(1 + sukuBunga / frekuensi, (frekuensi * tahun).toDouble())
}
Tag KDoc yang Tersedia #
KDoc mendukung berbagai tag untuk mendokumentasikan aspek yang berbeda dari sebuah deklarasi:
| Tag | Kegunaan | Contoh |
|---|---|---|
@param nama | Mendokumentasikan parameter fungsi | @param id ID unik pengguna |
@return | Menjelaskan nilai yang dikembalikan | @return null jika tidak ditemukan |
@throws KelasException | Mendokumentasikan exception yang mungkin dilempar | @throws IOException jika file tidak bisa dibaca |
@property nama | Mendokumentasikan property di data class | @property nama Nama lengkap pengguna |
@constructor | Mendokumentasikan primary constructor | @constructor Buat instance dengan konfigurasi awal |
@see | Referensi ke deklarasi atau URL lain | @see Pengguna |
@since | Versi sejak fitur ini tersedia | @since 1.2.0 |
@suppress | Menyembunyikan peringatan IDE tertentu | @suppress("UNCHECKED_CAST") |
@sample | Menyertakan contoh kode dari tempat lain | @sample contoh.PenggunaContoh.buatPengguna |
KDoc untuk Kelas #
/**
* Representasi seorang pengguna dalam sistem.
*
* Kelas ini adalah data class yang digunakan sebagai model utama
* untuk operasi CRUD pengguna. Setiap pengguna memiliki ID unik
* yang di-generate saat pembuatan dan tidak bisa diubah.
*
* @property id ID unik pengguna — di-generate otomatis oleh database
* @property nama Nama lengkap pengguna, minimal 2 karakter
* @property email Alamat email terverifikasi, harus unik di seluruh sistem
* @property peran Peran pengguna dalam sistem, default [Peran.PENGGUNA]
* @property aktif Status aktif akun, false berarti akun di-suspend
*
* @see PenggunaRepository
* @see Peran
* @since 1.0.0
*/
data class Pengguna(
val id: Long,
val nama: String,
val email: String,
val peran: Peran = Peran.PENGGUNA,
val aktif: Boolean = true
)
/**
* Peran yang tersedia dalam sistem otorisasi.
*
* @property ADMIN Akses penuh ke seluruh fitur dan manajemen pengguna
* @property MODERATOR Bisa mengelola konten tapi tidak bisa mengubah pengguna lain
* @property PENGGUNA Akses standar ke fitur yang tersedia untuk publik
*/
enum class Peran {
ADMIN, MODERATOR, PENGGUNA
}
KDoc untuk Interface #
/**
* Kontrak untuk semua repository yang mengelola data pengguna.
*
* Implementasi interface ini harus thread-safe karena bisa dipanggil
* dari coroutine yang berjalan di thread berbeda secara bersamaan.
*
* @see Pengguna
* @see PenggunaRepositoryImpl
*/
interface PenggunaRepository {
/**
* Mencari pengguna berdasarkan ID-nya.
*
* @param id ID pengguna yang dicari
* @return Objek [Pengguna] jika ditemukan, null jika tidak ada
*/
suspend fun cariById(id: Long): Pengguna?
/**
* Mencari pengguna berdasarkan alamat email.
*
* Pencarian bersifat case-insensitive — "[email protected]" dan
* "[email protected]" dianggap email yang sama.
*
* @param email Alamat email yang dicari
* @return Objek [Pengguna] jika ditemukan, null jika tidak ada
*/
suspend fun cariByEmail(email: String): Pengguna?
/**
* Menyimpan pengguna baru ke dalam database.
*
* @param pengguna Data pengguna yang akan disimpan. Field [Pengguna.id]
* akan diabaikan — ID di-generate oleh database.
* @return Objek [Pengguna] lengkap dengan ID yang baru di-generate
* @throws IllegalArgumentException jika email sudah terdaftar
* @throws IllegalArgumentException jika nama lebih pendek dari 2 karakter
*/
suspend fun simpan(pengguna: Pengguna): Pengguna
/**
* Menghapus pengguna secara permanen dari database.
*
* Operasi ini tidak bisa di-undo. Semua data terkait pengguna
* (profil, riwayat transaksi, dll) ikut terhapus karena foreign key
* constraint dengan CASCADE DELETE.
*
* @param id ID pengguna yang akan dihapus
* @return true jika pengguna berhasil dihapus, false jika ID tidak ditemukan
*/
suspend fun hapus(id: Long): Boolean
}
Formatting Teks dalam KDoc #
KDoc mendukung subset dari Markdown untuk memformat teks deskripsi:
/**
* Memvalidasi kekuatan password berdasarkan kriteria berikut:
*
* - Minimal **8 karakter**
* - Mengandung minimal satu **huruf besar**
* - Mengandung minimal satu **angka**
* - Mengandung minimal satu **karakter spesial** (`!@#$%^&*`)
*
* Contoh penggunaan:
* ```kotlin
* val kuat = validasiPassword("P@ssw0rd!") // true
* val lemah = validasiPassword("password") // false
* ```
*
* > **Catatan:** Fungsi ini hanya memvalidasi format, bukan memeriksa
* > apakah password pernah bocor di database credential leak.
*
* @param password Password yang akan divalidasi
* @return true jika memenuhi semua kriteria, false jika tidak
* @see [HaveIBeenPwned](https://haveibeenpwned.com/API/v3) untuk cek credential leak
*/
fun validasiPassword(password: String): Boolean {
val hurufBesar = Regex("[A-Z]")
val angka = Regex("[0-9]")
val karakterSpesial = Regex("[!@#\$%^&*]")
return password.length >= 8 &&
hurufBesar.containsMatchIn(password) &&
angka.containsMatchIn(password) &&
karakterSpesial.containsMatchIn(password)
}
Referensi Silang dalam KDoc #
Kamu bisa mereferensikan kelas, fungsi, atau property lain dengan notasi [NamaDeklarasi]:
/**
* Mengirim notifikasi ke semua pengguna dengan peran [Peran.ADMIN].
*
* Fungsi ini memanggil [kirimEmail] secara internal untuk setiap admin.
* Pastikan konfigurasi SMTP sudah benar di [KonfigurasiEmail] sebelum
* memanggil fungsi ini.
*
* @param pesan Isi notifikasi yang akan dikirim
* @throws [EmailException] jika koneksi SMTP gagal
*/
fun notifikasiAdmin(pesan: String) {
// implementasi...
}
Menghasilkan Dokumentasi dengan Dokka #
Dokka adalah tool resmi untuk menghasilkan dokumentasi HTML dari KDoc. Cara menggunakannya di proyek Gradle:
Tambahkan plugin ke build.gradle.kts:
plugins {
kotlin("jvm") version "2.0.0"
id("org.jetbrains.dokka") version "1.9.20"
}
Generate dokumentasi:
./gradlew dokkaHtml
Hasilnya berupa situs HTML lengkap di build/dokka/html/ yang bisa di-host di mana saja — GitHub Pages, internal server, atau mana pun.
flowchart LR
A["File .kt\n(dengan KDoc)"] --> B["Dokka\nProcessor"]
B --> C["HTML\nbuild/dokka/html/"]
B --> D["Markdown\nbuild/dokka/gfm/"]
B --> E["Javadoc format\nbuild/dokka/javadoc/"]
C --> F["GitHub Pages /\nInternal Server"]Filosofi Komentar yang Baik #
Memahami sintaks komentar hanyalah separuh dari ceritanya. Separuh lagi adalah tahu kapan menulis komentar dan apa yang layak dikomentar.
Kode yang Baik Adalah Dokumentasi Terbaik #
Nama variabel, fungsi, dan kelas yang deskriptif mengurangi kebutuhan komentar secara drastis.
// ANTI-PATTERN: nama tidak deskriptif, butuh komentar untuk menjelaskan
// Hitung d berdasarkan formula bisnis
fun calc(p: Double, r: Double, t: Int): Double {
return p * Math.pow(1 + r, t.toDouble())
}
// BENAR: nama yang deskriptif — komentar tidak diperlukan
fun hitungNilaiAkhirInvestasi(modalAwal: Double, sukuBungaTahunan: Double, tahun: Int): Double {
return modalAwal * Math.pow(1 + sukuBungaTahunan, tahun.toDouble())
}
Jelaskan “Mengapa”, Bukan “Apa” #
// ANTI-PATTERN: menjelaskan apa yang sudah jelas
val batasHalaman = 20 // set batas halaman ke 20
// BENAR: menjelaskan mengapa angka ini dipilih
val batasHalaman = 20 // dikalibrasi berdasarkan median waktu scroll pengguna mobile — lihat riset UX Q3
// ANTI-PATTERN: menjelaskan yang sudah terlihat dari kode
// Loop dari 0 sampai panjang list
for (i in 0 until daftar.size) { ... }
// BENAR: tidak perlu komentar sama sekali jika kodenya sudah jelas
for (item in daftar) { ... }
Komentar sebagai Tanda Bahaya #
Jika kamu merasa perlu komentar panjang untuk menjelaskan sepotong kode, itu sering kali sinyal bahwa kodenya perlu di-refactor.
// ANTI-PATTERN: komentar panjang menutupi kode yang kompleks
// Fungsi ini mengambil data dari cache jika ada,
// kalau tidak ada ambil dari database, simpan ke cache,
// kemudian kembalikan hasilnya, tapi kalau database juga error,
// coba fallback ke file lokal, kalau semua gagal return null
fun ambilData(id: String): Data? {
// ... 80 baris kode kompleks ...
}
// BENAR: ekstrak ke fungsi-fungsi kecil yang namanya berbicara sendiri
fun ambilData(id: String): Data? {
return ambilDariCache(id)
?: ambilDariDatabaseDanCache(id)
?: ambilDariFallbackLokal(id)
}
TODO dan FIXME #
Kotlin (dan IntelliJ IDEA) mengenali komentar dengan prefix khusus sebagai penanda tugas:
// TODO: tambahkan validasi input sebelum memanggil API eksternal
fun kirimData(payload: String) { ... }
// FIXME: ada race condition di sini ketika dua thread memanggil ini bersamaan
fun updateKonter() { ... }
// HACK: workaround untuk bug di library pihak ketiga versi 2.3.1
// Hapus ini setelah mereka release fix di versi 2.4.0
val hasil = library.prosesData(input.trim() + "\n")
// NOTE: fungsi ini sengaja tidak menggunakan coroutine karena
// perlu diblok sampai selesai — konteks pemanggil mengharapkan nilai sinkron
fun ambilKonfigurasi(): Config { ... }
IntelliJ IDEA menampilkan semua TODO dan FIXME dalam panel khusus (View → Tool Windows → TODO), sehingga kamu bisa melacak semua pekerjaan yang tertunda tanpa perlu mencari manual di seluruh codebase.
Komentar di Berbagai Konteks #
Komentar di Header File #
Untuk file yang berisi fungsi-fungsi utilitas atau konstanta, komentar di awal file berguna untuk menjelaskan tujuan file secara keseluruhan.
/*
* Utilitas untuk operasi kriptografi yang digunakan di seluruh aplikasi.
*
* Semua fungsi di file ini menggunakan algoritma yang sudah diaudit keamanannya.
* JANGAN menambahkan implementasi kriptografi kustom — gunakan yang sudah ada di sini.
*
* Penulis: Tim Security
* Terakhir diperbarui: 2024-03
*/
package com.myapp.util.crypto
import javax.crypto.Cipher
// ...
Komentar di Blok Kompleks #
fun kompresiData(data: ByteArray): ByteArray {
val output = ByteArrayOutputStream()
// GZIPOutputStream harus di-flush dan close secara eksplisit
// sebelum mengambil bytes dari ByteArrayOutputStream.
// Memanggil output.toByteArray() sebelum gzip.close() menghasilkan
// data yang corrupt — ini perilaku yang tidak intuitif dari Java IO.
GZIPOutputStream(output).use { gzip ->
gzip.write(data)
}
return output.toByteArray()
}
Komentar untuk Regex #
Regex adalah salah satu konteks di mana komentar hampir selalu diperlukan karena sintaksnya tidak mudah dibaca.
// Format: +62-XXX-XXXX-XXXX atau 08XX-XXXX-XXXX
// Mendukung spasi, dash, atau tanpa pemisah
val REGEX_NOMOR_TELEPON_ID = Regex(
"""^(\+62|0)[0-9]{2,3}[-\s]?[0-9]{3,4}[-\s]?[0-9]{4}$"""
)
// Format email standar — tidak mendukung quoted string atau IP literal
// karena use case kami tidak memerlukannya
val REGEX_EMAIL = Regex("""^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$""")
Ringkasan #
- Tiga jenis komentar —
//untuk satu baris,/* */untuk multi-baris,/** */untuk KDoc. Masing-masing punya konteks yang tepat.- Komentar multi-baris Kotlin bisa tersarang — berbeda dari Java,
/* /* */ */valid di Kotlin. Ini berguna saat men-comment-out kode yang sudah mengandung komentar.- KDoc adalah standar dokumentasi resmi — gunakan
/** */dengan tag@param,@return,@throws, dan lainnya untuk mendokumentasikan public API. Ini dibaca IDE dan tool Dokka.- Jelaskan “mengapa”, bukan “apa” — komentar terbaik menjelaskan niat, alasan, atau konteks yang tidak terlihat dari kode itu sendiri. Kode yang sudah ekspresif tidak butuh komentar yang sekadar menerjemahkannya.
- Kode bersih mengurangi kebutuhan komentar — nama yang deskriptif untuk variabel, fungsi, dan kelas adalah dokumentasi terbaik. Jika kamu perlu komentar panjang untuk menjelaskan satu fungsi, pertimbangkan untuk me-refactor fungsinya.
- Hapus kode yang di-comment-out — simpan di Git history, bukan di codebase aktif. Komentar berupa kode mati menambah noise dan membingungkan.
- Gunakan TODO dan FIXME secara konsisten — IntelliJ IDEA dan sebagian besar tool CI bisa melacaknya secara otomatis, sehingga tidak ada tugas yang terlupakan.
- Dokka untuk dokumentasi otomatis — dengan plugin Dokka dan KDoc yang baik, kamu bisa generate situs dokumentasi HTML lengkap hanya dengan satu perintah Gradle.