Tidak semua tipe boleh dipakai sebagai parameter anotasi. Java membatasi tipe parameter anotasi pada primitif, String, enum, Class, anotasi lain, dan array dari tipe-tipe tersebut. Kotlin mengikuti batasan yang sama, tetapi menambahkan dukungan KClass sebagai padanan Class. Pada chapter ini kita akan membedah tipe parameter yang diperbolehkan, cara memberi default value, dan penggunaan KClass<Foo> untuk mereferensikan tipe tanpa instansiasi.

Tipe yang Diperbolehkan #

TipeContohCatatan
PrimitifInt, Long, Double, Boolean, Char, Byte, Short, FloatSesuai dengan primitif Java.
Stringval path: StringLiteral string aman dipakai.
Enumval level: LevelTipe enum dideklarasikan terpisah.
Anotasival penulis: PenulisAnotasi sebagai parameter anotasi lain.
KClass<*>val target: KClass<*>Padanan Class<?> di Java.
Arrayval tags: Array<String>Array dari salah satu tipe di atas.

Tipe kompleks seperti List<Int>, Map<String, Int>, atau data class tidak diperbolehkan. Alasannya: anotasi disimpan di bytecode sebagai konstanta — JVM hanya mendukung konstanta primitif, String, dan Class.

Default Value #

Parameter anotasi boleh memiliki default value. Default value juga harus berupa konstanta (primitif, String, enum, atau array literal). Default value berguna agar pemakaian anotasi lebih ringkas — tidak semua argumen harus ditulis.

Contoh Kode #

Kode berikut mendemonstrasikan tiga tipe parameter sekaligus default value:

kotlin
// File: annotation_parameters.kt
// Mendemonstrasikan tipe parameter yang diperbolehkan pada anotasi Kotlin.
package com.example.param

// Enum boleh dipakai sebagai parameter anotasi.
enum class Level { DEBUG, INFO, WARN, ERROR }

// Anotasi lain boleh dipakai sebagai parameter.
annotation class Penulis(val nama: String)

// Tipe KClass<T> dapat dipakai untuk mereferensikan tipe tanpa instansiasi.
annotation class DiserialisasiSebagai(val target: kotlin.reflect.KClass<*>)

// Array dari tipe primitif juga diperbolehkan.
annotation class Rute(val path: String, val methods: Array<String> = ["GET"])

annotation class Log(
    val level: Level = Level.INFO,
    val penulis: Penulis = Penulis("anonim"),
    val tag: Array<String> = []
)

@Penulis("Budi")
@Rute("/api/pengguna", methods = ["GET", "POST"])
@DiserialisasiSebagai(String::class)
@Log(level = Level.WARN, tag = ["servis", "audit"])
class ApiPengguna

fun main() {
    val kelas = ApiPengguna::class

    val penulis = kelas.annotations.filterIsInstance<Penulis>().first()
    println("Penulis: ${penulis.nama}")

    val rute = kelas.annotations.filterIsInstance<Rute>().first()
    println("Rute: ${rute.path} methods=${rute.methods.toList()}")

    val ser = kelas.annotations.filterIsInstance<DiserialisasiSebagai>().first()
    println("Serialisasi sebagai: ${ser.target.simpleName}")

    val log = kelas.annotations.filterIsInstance<Log>().first()
    println("Log level: ${log.level}, penulis: ${log.penulis.nama}, tag: ${log.tag.toList()}")
}

Output yang Dihasilkan:

text
Penulis: Budi
Rute: /api/pengguna methods=[GET, POST]
Serialisasi sebagai: String
Log level: WARN, penulis: anonim, tag: [servis, audit]

Mari kita amati baris-baris penting di atas:

  1. Baris 5 (enum class Level): enum yang akan dipakai sebagai parameter. Enum harus dideklarasikan terpisah (tidak boleh nested di dalam anotasi).
  2. Baris 8 (annotation class Penulis): anotasi lain menjadi parameter anotasi. Pola ini sering muncul pada @Log(penulis = Penulis("Budi")).
  3. Baris 11 (val target: kotlin.reflect.KClass<*>): KClass<*> adalah padanan Class<?> di Java. Pemakaian String::class menghasilkan KClass<String>. Kompilator menyimpan tipe sebagai Class di bytecode.
  4. Baris 14 (val methods: Array<String> = ["GET"]): array dengan default value. Literal array [...] didukung sejak Kotlin 1.2 untuk anotasi.
  5. Baris 17-21 (annotation class Log): tiga parameter masing-masing dengan default. Saat @Log(level = Level.WARN, tag = ["servis", "audit"]), parameter penulis otomatis memakai default Penulis("anonim").
  6. Baris 31 (ser.target.simpleName): mengakses KClass<*> via target. Properti simpleName memberikan nama class tanpa package.

KClass vs Class #

Pada Kotlin murni, gunakan KClass<T>. Kompilator akan menyimpannya sebagai Class di bytecode JVM sehingga tetap kompatibel dengan kode Java yang membaca anotasi. Bila anotasi akan dipakai Java secara langsung, pertimbangkan menambahkan @JvmField atau mengekspos sebagai Class<*> lewat annotation property.

Array Literal #

Sejak Kotlin 1.2, anotasi mendukung literal array [...] seperti pada methods = ["GET", "POST"]. Sebelumnya kita harus menulis arrayOf("GET", "POST"). Literal array hanya berlaku dalam anotasi; di luar anotasi kita tetap memakai arrayOf atau listOf.

Konvensi Parameter #

Beberapa konvensi yang umum:

  • Parameter tanpa default sebaiknya parameter pertama (mirip path pada @Rute).
  • Parameter opsional taruh di belakang dengan default value.
  • Gunakan Array<String> untuk daftar tag atau rute; jangan pakai List<String> karena tidak didukung.

Referensi Lanjutan #

  • Annotations — bagian parameter anotasi dan tipe yang diperbolehkan pada panduan resmi

Selanjutnya #

Setelah memahami deklarasi anotasi, target, retention, dan parameter, pertanyaan berikutnya adalah bagaimana anotasi diproses. Chapter Annotation Processing akan membandingkan tiga jalur pemrosesan: KSP, KAPT, dan reflection.