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:

TargetBoleh menempel pada
CLASSClass, interface, object, annotation class.
ANNOTATION_CLASSKhusus annotation class.
PROPERTYDeklarasi property.
FIELDField backing property.
LOCAL_VARIABLEVariabel lokal.
VALUE_PARAMETERParameter function/constructor.
EXPRESSIONEkspresi tunggal (jarang dipakai).
FILEFile (dengan @file: di baris pertama).
TYPETipe (mis. pada parameter generic atau type alias).
TYPE_PARAMETERParameter tipe generic.
FUNCTIONFunction dan constructor.
CONSTRUCTORKhusus constructor.
PROPERTY_GETTERGetter property.
PROPERTY_SETTERSetter property.

Mendeklarasikan @Target #

Beberapa target sekaligus dapat dipasang dengan koma sebagai pemisah. Kompilator menolak pemakaian di luar daftar target. Berikut contoh konkret:

kotlin
// 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:

text
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_SETTER

Mari kita amati baris-baris penting di atas:

  1. Baris 6 (@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)): Layanan boleh dipasang pada class atau function. Mencoba memasang pada property memicu galat kompilasi.
  2. Baris 11 (@Target(AnnotationTarget.PROPERTY)): Disuntik khusus property. Konsep ini mirip dengan @Inject pada framework DI.
  3. Baris 16 (@Target(AnnotationTarget.VALUE_PARAMETER)): Header hanya boleh menempel pada parameter function. Cocok untuk menandai header HTTP pada endpoint.
  4. Baris 21 (@Target(AnnotationTarget.PROPERTY_GETTER)): Diukur dipasang pada getter property. Kompilator memerlukan use-site target @get: agar anotasi tidak salah ditempatkan pada property (default) atau field.
  5. Baris 30 (@get:Diukur): use-site target @get: memberitahu kompilator bahwa @Diukur menempel 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.
  6. Baris 35 (@Header("X-Request-ID") requestId: String): anotasi pada parameter function. Pada bytecode JVM, anotasi parameter memerlukan flag -parameters agar 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:

PrefixTarget
@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:

kotlin @file:JvmName
@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 @Target dan AnnotationTarget pada 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.