Kotlin進階-註解(與java有點變化)

在這裏插入圖片描述

前言

有些定義總是需要註解去實現的。
本文需要帶着幾個問題
① 怎麼定義Kotlin的註解
② 註解類的屬性有幾種,什麼用途
③ 可以在哪些地方用註解
④ 與java註解怎麼交互
⑤ 文中小思:爲什麼kotlin與java交互,要用到KClass,然後轉換成java的class


註解聲明

簡介

要定義一個註解類,需要將 annotation 修飾符放在類的前面

annotation class Fancy
附加屬性

可以通過用元註解標註註解類來指定

屬性類型
  • @Target
    指定可以用該註解標註的元素的可能的類型(類、函數、屬性、表達式等);
  • @Retention
    指定該註解是否存儲在編譯後的 class 文件中,以及它在運行時能否通過反射可見 (默認都是 true);
  • @Repeatable
    允許在單個元素上多次使用相同的該註解;
  • @MustBeDocumented
    指定該註解是公有 API 的一部分,並且應該包含在生成的 API 文檔中顯示的類或方法的簽名中。
範例
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
        AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Fancy

用法

先看個範例

@Fancy class Foo {
    @Fancy fun baz(@Fancy foo: Int): Int {
        return (@Fancy 1)
    }
}
支持 標註 主構造函數

看下面的範例

class Foo @Inject constructor(dependency: MyDependency) { …… }
支持 標註 屬性訪問器

看下面的範例

class Foo {
    var x: MyDependency? = null
        @Inject set
}

構造函數

註解也可以帶參數
annotation class Special(val why: String)

@Special("example") class Foo {}
允許的參數類型
  • 對應於 Java 原生類型的類型(Int、 Long等);
  • 字符串;
  • 類(Foo::class);
  • 枚舉;
  • 其他註解;
  • 上面已列類型的數組。
注意

註解參數不能有可空類型

JVM 不支持將 null 作爲註解屬性的值存儲

註解支持嵌套使用

即 註解A 用作註解B 的參數
其名稱不以 @ 字符爲前綴

看個範例
annotation class ReplaceWith(val expression: String)

annotation class Deprecated(
        val message: String,
        val replaceWith: ReplaceWith = ReplaceWith(""))

@Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other"))
一個類 也可以 作用註解的參數

這需要使用 Kotlin 類 (KClass)

然後,Kotlin 編譯器會自動將其轉換爲 Java 類。

等等,爲何要轉變成java類?
在這裏插入圖片描述
進一步理解一下
爲什麼最終還是java類呢?再想一下
爲什麼Kotlin能兼容java呢?
我們可以大膽假設一下,Kotlin的兼容,其實是在編譯期的。編譯器將Kotlin轉換成java,所以,支持了java。

細心的朋友應該有注意到
很多Kotlin實現的東西,其實用java繞來繞去也可以實現。Kotlin勝在簡便。而這個簡便到底是怎麼來的呢?
是編譯器的轉換。編譯器處理了之前需要手動實現的繁雜的事務。
這也是爲什麼Kotlin能很好的兼容Android、IOS、JavaScript的原因。

Kotlin是Java、OC、JS包裝後的實現

範例
import kotlin.reflect.KClass

annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any>)

@Ann(String::class, Int::class) class MyClass

Lambda 表達式

註解也可以用於 lambda 表達式
會被應用於生成 lambda 表達式體的 invoke() 方法

看範例,使用註解的併發控制
annotation class Suspendable
val f = @Suspendable { Fiber.sleep(10) }

註解使用處目標

註解可以通過一系列@XXX更精確的生成所需註解

class Example(@field:Ann val foo,    // 標註 Java 字段
              @get:Ann val bar,      // 標註 Java getter
              @param:Ann val quux)   // 標註 Java 構造函數參數

支持的使用處目標的完整列表

  • file;
  • property(具有此目標的註解對 Java 不可見);
  • field;
  • get(屬性 getter);
  • set(屬性 setter);
  • receiver(擴展函數或屬性的接收者參數);
  • param(構造函數參數);
  • setparam(屬性 setter 參數);
  • delegate(爲委託屬性存儲其委託實例的字段)。

若希望使用相同的預發標注整個文件

@file:JvmName("Foo")

package org.jetbrains.demo

把帶有目標 file 的註解放在文件的頂層、package 指令之前或者在所有導入之前

標註 擴展函數的接收者 參數

fun @receiver:Fancy String.myExtension() { ... }

哪裏又冒出來個接收者的概念。部分同學就費解了。

接收者參數是什麼呢?

這個說法在GoLang看到比較多
顧名思義,就是 被賦值、被傳參、被XXX的

範例
fun notify(User user) {}

notify它是值接收者方法


Java 註解

Java 註解與 Kotlin 100% 兼容

我們來看個範例對比
// Java

public @interface Ann {
    int intValue();
    String stringValue();
}

// Kotlin

@Ann(intValue = 1, stringValue = "abc") class C
注意

若參數只有一個value()
它的值無需顯式名稱指定

看個範例
java

public @interface AnnWithValue {
    String value();
}

Kotlin

@AnnWithValue("abc") class C

數組作爲註解參數

如果 Java 中的 value 參數具有數組類型

// Java

public @interface AnnWithArrayValue {
    String[] value();
}

value()是可以省略 顯式的使用 參數名的:value

// Kotlin

@AnnWithArrayValue("abc", "foo", "bar") class C

如果這裏value換個名字,改成names,則需要顯式的使用

範例
// Java
public @interface AnnWithArrayMethod {
    String[] names();
}
// Kotlin 1.2+:
@AnnWithArrayMethod(names = ["abc", "foo", "bar"]) 
class C

// 舊版本 Kotlin:
@AnnWithArrayMethod(names = arrayOf("abc", "foo", "bar")) 
class D

訪問註解實例的屬性

註解實例的值會作爲屬性暴露給 Kotlin 代碼
java的註解會以屬性的方式暴露給kotlin

範例
// Java
public @interface Ann {
    int value();
}
// Kotlin
fun foo(ann: Ann) {
    val i = ann.value
}

小結

回過頭來,再看五個問題

① 怎麼定義Kotlin的註解
② 註解類的屬性有幾種,什麼用途
③ 可以在哪些地方用註解
④ 與java註解怎麼交互
⑤ 文中小思:爲什麼kotlin與java交互,要用到KClass,然後轉換成java的class

是否有答案了呢

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章