🏷️ Annotations
1. Giới thiệu
Phần tiêu đề “1. Giới thiệu”Annotations là metadata được gắn vào code. Chúng không thay đổi behavior trực tiếp nhưng có thể được đọc bởi compiler, tools, hoặc runtime để xử lý đặc biệt.
2. Sử dụng Annotations
Phần tiêu đề “2. Sử dụng Annotations”@Deprecated("Use newFunction() instead", ReplaceWith("newFunction()"))fun oldFunction() { println("Old implementation")}
fun newFunction() { println("New implementation")}
@Suppress("UNCHECKED_CAST")fun unsafeCast(obj: Any): String { return obj as String}
fun main() { oldFunction() // Warning: deprecated}3. Built-in Annotations
Phần tiêu đề “3. Built-in Annotations”Kotlin Annotations
Phần tiêu đề “Kotlin Annotations”// @Deprecated - đánh dấu deprecated@Deprecated( message = "Use newApi() instead", replaceWith = ReplaceWith("newApi()"), level = DeprecationLevel.WARNING // WARNING, ERROR, HIDDEN)fun oldApi() {}
// @Suppress - suppress warnings@Suppress("UNUSED_PARAMETER", "UNUSED_VARIABLE")fun example(unused: String) { val x = 5}
// @OptIn - opt-in to experimental APIs@OptIn(ExperimentalStdlibApi::class)fun useExperimental() { // Use experimental APIs}
// @DslMarker - for DSL type safety@DslMarkerannotation class HtmlDsl
// @RequiresOptIn - mark API as experimental@RequiresOptIn(message = "This API is experimental")annotation class ExperimentalFeatureJVM Interop Annotations
Phần tiêu đề “JVM Interop Annotations”class MyClass { companion object { // @JvmStatic - generate static method for Java @JvmStatic fun staticMethod() { println("Can be called as MyClass.staticMethod() in Java") }
// @JvmField - expose as public field @JvmField val CONSTANT = "VALUE" }
// @JvmOverloads - generate overloaded methods @JvmOverloads fun greet(name: String = "World", greeting: String = "Hello") { println("$greeting, $name!") }
// @JvmName - custom JVM name @get:JvmName("getFullNameProperty") val fullName: String = "John Doe"
// @Throws - declare exceptions for Java @Throws(IOException::class) fun readFile(path: String): String { return java.io.File(path).readText() }}@JvmInline cho Value Classes
Phần tiêu đề “@JvmInline cho Value Classes”@JvmInlinevalue class Password(val value: String)4. Annotation Targets
Phần tiêu đề “4. Annotation Targets”Chỉ định vị trí áp dụng annotation:
// file target@file:JvmName("Utils")
package com.example
// property targetclass Example { @property:Deprecated("Old property") val oldProp: String = ""
// field target (backing field) @field:Volatile var counter: Int = 0
// getter target @get:JvmName("fetchValue") val value: Int = 0
// setter target var name: String = "" @set:Synchronized set
// param target fun process(@param:NotNull input: String) {}
// receiver target fun @receiver:NotNull String.extend() {}}Use-site targets
Phần tiêu đề “Use-site targets”| Target | Áp dụng cho |
|---|---|
file: |
Entire file |
property: |
Property (Kotlin) |
field: |
Backing field |
get: |
Property getter |
set: |
Property setter |
param: |
Constructor/function param |
receiver: |
Extension receiver |
delegate: |
Delegate field |
5. Defining Custom Annotations
Phần tiêu đề “5. Defining Custom Annotations”Basic annotation
Phần tiêu đề “Basic annotation”annotation class MyAnnotation
@MyAnnotationclass MyClassAnnotation với properties
Phần tiêu đề “Annotation với properties”annotation class Info( val author: String, val version: Int, val description: String = "" // default value)
@Info(author = "Alice", version = 1, description = "Example class")class Example
// Array valuesannotation class Tags(val values: Array<String>)
@Tags(["kotlin", "android", "tutorial"])class TutorialMeta-annotations
Phần tiêu đề “Meta-annotations”// @Target - where annotation can be applied@Target( AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)annotation class Auditable
// @Retention - when annotation is available@Retention(AnnotationRetention.RUNTIME) // Available at runtimeannotation class RuntimeAnnotation
@Retention(AnnotationRetention.BINARY) // In bytecode, not at runtimeannotation class BinaryAnnotation
@Retention(AnnotationRetention.SOURCE) // Only in source codeannotation class SourceAnnotation
// @Repeatable - can be applied multiple times@Repeatableannotation class Author(val name: String)
@Author("Alice")@Author("Bob")class Book
// @MustBeDocumented - include in documentation@MustBeDocumentedannotation class Documented(val description: String)6. Annotation Processing (Compile-time)
Phần tiêu đề “6. Annotation Processing (Compile-time)”Ví dụ: Custom lint annotation
Phần tiêu đề “Ví dụ: Custom lint annotation”@Target(AnnotationTarget.FUNCTION)@Retention(AnnotationRetention.SOURCE)annotation class MainThread
@Target(AnnotationTarget.FUNCTION)@Retention(AnnotationRetention.SOURCE)annotation class WorkerThread
class NetworkService { @WorkerThread fun fetchData(): String { // Must be called on background thread return "data" }
@MainThread fun updateUi(data: String) { // Must be called on main thread }}Note: Annotation processing cần thêm annotation processor (như KAPT hoặc KSP).
7. Annotations trong Android
Phần tiêu đề “7. Annotations trong Android”class MainActivity : AppCompatActivity() { // View binding/injection // @BindView(R.id.textView) lateinit var textView: TextView // ButterKnife
// Dagger/Hilt // @Inject lateinit var repository: UserRepository
// Room Database // @Entity(tableName = "users") // data class User(@PrimaryKey val id: Int, val name: String)
// Serialization // @Serializable data class ApiResponse(val data: String)}
// Compose// @Composable fun MyScreen() { }// @Preview @Composable fun PreviewMyScreen() { }8. Kotlinx Serialization Annotations
Phần tiêu đề “8. Kotlinx Serialization Annotations”import kotlinx.serialization.*import kotlinx.serialization.json.*
@Serializabledata class User( @SerialName("user_id") val id: Int,
val name: String,
@Transient // Excluded from serialization val password: String = "",
@EncodeDefault val role: String = "user")
fun main() { val user = User(1, "Alice", "secret123") val json = Json.encodeToString(user) println(json) // {"user_id":1,"name":"Alice","role":"user"}}9. Reading Annotations at Runtime
Phần tiêu đề “9. Reading Annotations at Runtime”import kotlin.reflect.full.*
@Target(AnnotationTarget.CLASS)@Retention(AnnotationRetention.RUNTIME)annotation class Entity(val tableName: String)
@Target(AnnotationTarget.PROPERTY)@Retention(AnnotationRetention.RUNTIME)annotation class Column(val name: String, val primaryKey: Boolean = false)
@Entity("users")data class User( @Column("id", primaryKey = true) val id: Int,
@Column("user_name") val name: String,
@Column("email_address") val email: String)
fun generateCreateTable(clazz: kotlin.reflect.KClass<*>): String { val entity = clazz.findAnnotation<Entity>() ?: throw IllegalArgumentException("Missing @Entity annotation")
val columns = clazz.memberProperties .mapNotNull { prop -> prop.findAnnotation<Column>()?.let { col -> "${col.name} TEXT${if (col.primaryKey) " PRIMARY KEY" else ""}" } } .joinToString(", ")
return "CREATE TABLE ${entity.tableName} ($columns)"}
fun main() { val sql = generateCreateTable(User::class) println(sql) // CREATE TABLE users (id TEXT PRIMARY KEY, user_name TEXT, email_address TEXT)}10. Best Practices
Phần tiêu đề “10. Best Practices”// ✅ Dùng Target để restrict usage@Target(AnnotationTarget.FUNCTION)annotation class NeedsPermission(val permission: String)
// ✅ Dùng Retention phù hợp@Retention(AnnotationRetention.RUNTIME) // Cần đọc at runtimeannotation class Injectable
@Retention(AnnotationRetention.SOURCE) // Chỉ cho compiler/toolsannotation class SuppressLint(val value: String)
// ✅ Document annotations@MustBeDocumented@Target(AnnotationTarget.FUNCTION)annotation class Authenticated( val roles: Array<String> = [])
// ✅ Combine related annotations@Target(AnnotationTarget.FUNCTION)@Retention(AnnotationRetention.RUNTIME)annotation class SecureEndpoint( val authenticated: Boolean = true, val roles: Array<String> = [], val rateLimit: Int = 100)📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Meta-annotation | Purpose |
|---|---|
@Target |
Nơi annotation có thể được apply |
@Retention |
Khi annotation available (SOURCE/BINARY/RUNTIME) |
@Repeatable |
Cho phép apply nhiều lần |
@MustBeDocumented |
Include trong documentation |
Common JVM annotations:
@JvmStatic- Static methods cho Java@JvmField- Public fields cho Java@JvmOverloads- Generate overloaded methods@JvmName- Custom JVM name@Throws- Declare exceptions
Best practices:
- Dùng
RUNTIMEretention khi cần đọc via reflection - Dùng
SOURCEcho tool-only annotations - Always specify
@Targetđể prevent misuse