Anotasi yang ditujukan untuk class sebaiknya tidak sengaja menempel pada function, begitu pula sebaliknya. Untuk membatasi tempat anotasi boleh dipasang, Kotlin menyediakan meta-annotation @Target. Pada chapter ini kita akan membedah seluruh anggota AnnotationTarget, mendemonstrasikan anotasi pada beberapa target sekaligus, dan menjelaskan penggunaan use-site target seperti @get: dan @field: untuk menempatkan anotasi pada bagian spesifik properti.
Daftar AnnotationTarget #
Berikut semua anggota AnnotationTarget di Kotlin:
| Target | Boleh menempel pada |
|---|---|
CLASS | Class, interface, object, annotation class. |
ANNOTATION_CLASS | Khusus annotation class. |
PROPERTY | Deklarasi property. |
FIELD | Field backing property. |
LOCAL_VARIABLE | Variabel lokal. |
VALUE_PARAMETER | Parameter function/constructor. |
EXPRESSION | Ekspresi tunggal (jarang dipakai). |
FILE | File (dengan @file: di baris pertama). |
TYPE | Tipe (mis. pada parameter generic atau type alias). |
TYPE_PARAMETER | Parameter tipe generic. |
FUNCTION | Function dan constructor. |
CONSTRUCTOR | Khusus constructor. |
PROPERTY_GETTER | Getter property. |
PROPERTY_SETTER | Setter property. |
Mendeklarasikan @Target #
Beberapa target sekaligus dapat dipasang dengan koma sebagai pemisah. Kompilator menolak pemakaian di luar daftar target. Berikut contoh konkret:
// File: annotation_targets.kt
// Mendemonstrasikan target anotasi via @Target dan AnnotationTarget.
package com.example.target
// Target boleh lebih dari satu, dipisah koma.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Layanan
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class Disuntik(val nama: String)
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Header(val kunci: String)
@Target(AnnotationTarget.PROPERTY_GETTER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Diukur
// CLASS: ditempel pada class.
@Layanan
class ApiPengguna {
// PROPERTY: ditempel pada deklarasi property.
@Disuntik("repoPengguna")
private val repo: String = "default-repo"
// PROPERTY_GETTER: pakai use-site target @get: untuk menerapkan ke getter.
@get:Diukur
val ukuran: Int = 42
// VALUE_PARAMETER: ditempel pada parameter function.
fun cari(@Header("X-Request-ID") requestId: String): String {
return "cari dengan request $requestId"
}
}
fun main() {
val kelas = ApiPengguna::class
val adaLayanan = kelas.annotations.filterIsInstance<Layanan>().isNotEmpty()
println("ApiPengguna punya @Layanan: $adaLayanan")
// Ambil anotasi pada parameter function `cari`.
val cariMethod = kelas.java.getDeclaredMethod("cari", String::class.java)
for (param in cariMethod.parameters) {
val header = param.annotations.filterIsInstance<Header>().firstOrNull()
if (header != null) {
println("Parameter ${param.name} punya @Header(kunci=\"${header.kunci}\")")
}
}
// Ambil anotasi pada getter property `ukuran`.
val getterUkuran = ApiPengguna::ukuran.getter
val diukur = getterUkuran.annotations.filterIsInstance<Diukur>().isNotEmpty()
println("getter properti ukuran punya @Diukur: $diukur")
println("Daftar AnnotationTarget yang umum dipakai:")
println(" CLASS, ANNOTATION_CLASS, PROPERTY, FIELD,")
println(" LOCAL_VARIABLE, VALUE_PARAMETER, EXPRESSION, FILE,")
println(" TYPE, TYPE_PARAMETER, FUNCTION, CONSTRUCTOR,")
println(" PROPERTY_GETTER, PROPERTY_SETTER")
}Output yang Dihasilkan:
ApiPengguna punya @Layanan: true
Parameter arg0 punya @Header(kunci="X-Request-ID")
getter properti ukuran punya @Diukur: true
Daftar AnnotationTarget yang umum dipakai:
CLASS, ANNOTATION_CLASS, PROPERTY, FIELD,
LOCAL_VARIABLE, VALUE_PARAMETER, EXPRESSION, FILE,
TYPE, TYPE_PARAMETER, FUNCTION, CONSTRUCTOR,
PROPERTY_GETTER, PROPERTY_SETTERMari kita amati baris-baris penting di atas:
- Baris 6 (
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)):Layananboleh dipasang pada class atau function. Mencoba memasang pada property memicu galat kompilasi. - Baris 11 (
@Target(AnnotationTarget.PROPERTY)):Disuntikkhusus property. Konsep ini mirip dengan@Injectpada framework DI. - Baris 16 (
@Target(AnnotationTarget.VALUE_PARAMETER)):Headerhanya boleh menempel pada parameter function. Cocok untuk menandai header HTTP pada endpoint. - Baris 21 (
@Target(AnnotationTarget.PROPERTY_GETTER)):Diukurdipasang pada getter property. Kompilator memerlukan use-site target@get:agar anotasi tidak salah ditempatkan pada property (default) atau field. - Baris 30 (
@get:Diukur): use-site target@get:memberitahu kompilator bahwa@Diukurmenempel pada getter, bukan pada property secara umum. Tanpa awalan ini, kompilator akan kesulitan menentukan target karena banyak anotasi Java (mis.@NotNull) dapat menempel pada beberapa posisi. - Baris 35 (
@Header("X-Request-ID") requestId: String): anotasi pada parameter function. Pada bytecode JVM, anotasi parameter memerlukan flag-parametersagar nama parameter terbaca (Java 8+).
Use-Site Target #
Ketika anotasi memiliki beberapa target yang mungkin (mis. @Target(PROPERTY, FIELD)), kita dapat menentukan target spesifik dengan use-site prefix:
| Prefix | Target |
|---|---|
@get: | Getter property. |
@set: | Setter property. |
@field: | Field backing property. |
@param: | Parameter konstruktor. |
@property: | Property (deklarasi). |
@file: | File (harus di baris paling atas, sebelum package). |
@type: | Tipe (mis. @type:TahanNull). |
Target FILE #
Target FILE berguna untuk metadata yang berlaku untuk seluruh file — misalnya @file:JvmName("UtilKt") untuk mengatur nama class Java hasil kompilasi:
@file:JvmName("StringUtil")
package com.example.util
fun kapital(s: String): String = s.uppercase()Dari sisi Java, function kapital akan menjadi static method pada class StringUtil (bukan StringUtilKt).
Referensi Lanjutan #
- Annotations
— bagian
@TargetdanAnnotationTargetpada panduan anotasi resmi
Selanjutnya #
@Target mengatur tempat anotasi boleh dipasang. Pertanyaan berikutnya: berapa lama anotasi itu bertahan? Jawabannya ada pada @Retention, yang akan kita bahas pada chapter Annotation Retention.