Kotlin menyediakan sejumlah anotasi bawaan yang memengaruhi kompilasi, interop Java, dan metadata runtime. Beberapa anotasi mengubah cara kompilator menghasilkan bytecode; yang lain sekadar menandai deklarasi agar dibaca reflection. Pada chapter ini kita akan membedah tujuh anotasi paling sering dipakai: @Deprecated, @JvmStatic, @JvmField, @JvmOverloads, @Nullable, @NonNull, serta meta-annotation @Target dan @Retention. Pemahaman ini menjadi fondasi sebelum kita membuat anotasi custom pada chapter berikutnya.

Tujuh Anotasi yang Sering Dipakai #

AnotasiTempelEfek
@DeprecatedFunction, property, classMenandai API lama; kompilator memunculkan warning saat dipanggil.
@JvmStaticMember object/companionMenghasilkan static method Java pada class pembungkus.
@JvmFieldPropertyMengekspos field langsung tanpa getter/setter Java.
@JvmOverloadsFunction dengan default argMenghasilkan overload Java untuk tiap kombinasi default.
@TargetDeklarasi anotasiMembatasi target pemasangan anotasi.
@RetentionDeklarasi anotasiMengatur apakah anotasi disimpan di SOURCE, BINARY, atau RUNTIME.
@Nullable / @NonNullTipeMenerangkan nullability untuk interop Java.

Contoh Kode #

Kode berikut memperlihatkan tujuh anotasi di atas dalam aksi:

kotlin
// File: built_in_annotations.kt
// Mendemonstrasikan anotasi bawaan Kotlin: @Deprecated, @JvmStatic,
// @JvmField, @JvmOverloads, @Target, @Retention.
package com.example.annotasi

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class LogPemakaian(val kategori: String)

object Konfigurasi {
    @JvmField
    val VERSI: String = "1.0.0"

    @JvmStatic
    fun ambilNamaAplikasi(): String = "Aplikasi Contoh"

    @JvmOverloads
    fun sapa(nama: String, salam: String = "Halo", tandaSeru: Boolean = false): String {
        val pesan = "$salam, $nama"
        return if (tandaSeru) "$pesan!" else pesan
    }
}

class RepoLama {
    @Deprecated(
        message = "Ganti dengan cari(id: Long) yang lebih cepat dan aman.",
        replaceWith = ReplaceWith("cari(id)"),
        level = DeprecationLevel.WARNING
    )
    fun cariLama(id: Long): String = "Pengguna lama #$id"

    fun cari(id: Long): String = "Pengguna baru #$id"
}

@LogPemakaian("servis")
class LayananPengguna {
    fun ambil(id: Long): String = "Pengguna#$id"
}

fun main() {
    println("Versi: ${Konfigurasi.VERSI}")
    println(Konfigurasi.ambilNamaAplikasi())
    println(Konfigurasi.sapa("Budi"))
    println(Konfigurasi.sapa("Siti", "Selamat pagi"))
    println(Konfigurasi.sapa("Dewi", "Hai", true))

    val repo = RepoLama()
    // Memakai API baru, bukan yang deprecated.
    println(repo.cari(101L))

    // Membaca anotasi @LogPemakaian via reflection.
    val kelas = LayananPengguna::class
    val anotasi = kelas.annotations.find { it is LogPemakaian }
    if (anotasi is LogPemakaian) {
        println("Anotasi ditemukan: kategori=${anotasi.kategori}")
    }
}

Output yang Dihasilkan:

text
Versi: 1.0.0
Aplikasi Contoh
Halo, Budi
Selamat pagi, Siti
Hai, Dewi!
Pengguna baru #101
Anotasi ditemukan: kategori=servis

Mari kita bedah baris-baris kunci di atas:

  1. Baris 6-7 (@Target + @Retention): dua anotasi ini adalah meta-annotation yang menentukan perilaku anotasi LogPemakaian. @Target membatasi tempat LogPemakaian boleh dipasang; @Retention menentukan apakah anotasi bertahan hingga runtime.
  2. Baris 11 (@JvmField val VERSI): menginstruksikan kompilator agar tidak membuat getter/setter Java untuk properti ini. Dari sisi Java, Konfigurasi.VERSI langsung diakses sebagai field public.
  3. Baris 14 (@JvmStatic fun ambilNamaAplikasi): menghasilkan static method pada class Java Konfigurasi (selain pada instance object). Java caller menulis Konfigurasi.ambilNamaAplikasi() tanpa harus melewati INSTANCE.
  4. Baris 17 (@JvmOverloads fun sapa): function memiliki tiga parameter dengan dua default value. @JvmOverloads membuat kompilator menghasilkan tiga overload Java: sapa(nama), sapa(nama, salam), dan sapa(nama, salam, tandaSeru). Tanpa anotasi ini, kode Java hanya melihat satu versi penuh.
  5. Baris 27 (@Deprecated): menandai cariLama sebagai API lama. replaceWith = ReplaceWith("cari(id)") memberikan saran otomatis yang dapat diaplikasikan IntelliJ IDEA. level = DeprecationLevel.WARNING memunculkan warning kompilasi; ERROR menjadikannya galat, HIDDEN menyembunyikan API dari completion.
  6. Baris 38 (@LogPemakaian("servis")): anotasi custom ditempel pada class. Karena retention RUNTIME, anotasi dapat dibaca via reflection pada baris 54-57.

@Deprecated dan Levelnya #

@Deprecated adalah anotasi paling sering dipakai pada library. Tiga level yang tersedia:

  • WARNING (default): kompilator menampilkan warning, kode tetap berjalan.
  • ERROR: kompilator menolak pemakaian; konsumen dipaksa bermigrasi.
  • HIDDEN: deklarasi tidak terlihat dari IDE completion, tetapi tetap ada di bytecode.

@JvmStatic dan @JvmField untuk Interop Java #

Saat library Kotlin dipanggil dari Java, kedua anotasi ini sangat berguna. @JvmStatic membuat method object/companion terlihat sebagai static method Java. @JvmField membuat property terlihat sebagai field public biasa. Tanpa keduanya, kode Java harus menulis Konfigurasi.INSTANCE.ambilNamaAplikasi() dan konfigurasi.getVERSI() — lebih verbose.

Referensi Lanjutan #

  • Annotations — panduan resmi anotasi bawaan Kotlin seperti @Deprecated, @JvmStatic, dan @JvmField

Selanjutnya #

Setelah mengenal anotasi bawaan, kita akan membuat anotasi sendiri — topik chapter Custom Annotations.