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 #
| Tipe | Contoh | Catatan |
|---|---|---|
| Primitif | Int, Long, Double, Boolean, Char, Byte, Short, Float | Sesuai dengan primitif Java. |
String | val path: String | Literal string aman dipakai. |
| Enum | val level: Level | Tipe enum dideklarasikan terpisah. |
| Anotasi | val penulis: Penulis | Anotasi sebagai parameter anotasi lain. |
KClass<*> | val target: KClass<*> | Padanan Class<?> di Java. |
| Array | val 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:
// 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:
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:
- Baris 5 (
enum class Level): enum yang akan dipakai sebagai parameter. Enum harus dideklarasikan terpisah (tidak boleh nested di dalam anotasi). - Baris 8 (
annotation class Penulis): anotasi lain menjadi parameter anotasi. Pola ini sering muncul pada@Log(penulis = Penulis("Budi")). - Baris 11 (
val target: kotlin.reflect.KClass<*>):KClass<*>adalah padananClass<?>di Java. PemakaianString::classmenghasilkanKClass<String>. Kompilator menyimpan tipe sebagaiClassdi bytecode. - Baris 14 (
val methods: Array<String> = ["GET"]): array dengan default value. Literal array[...]didukung sejak Kotlin 1.2 untuk anotasi. - Baris 17-21 (
annotation class Log): tiga parameter masing-masing dengan default. Saat@Log(level = Level.WARN, tag = ["servis", "audit"]), parameterpenulisotomatis memakai defaultPenulis("anonim"). - Baris 31 (
ser.target.simpleName): mengaksesKClass<*>viatarget. PropertisimpleNamememberikan 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
pathpada@Rute). - Parameter opsional taruh di belakang dengan default value.
- Gunakan
Array<String>untuk daftar tag atau rute; jangan pakaiList<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.