1.メタプログラミング
メタプログラミングはプログラミングにプログラミングされています。符号化技術、高レベルの抽象化自体の本質的にソース。
プログラミング、注釈では、メタプログラミングの良い上の反射を促進する
2つの方法でのメタプログラミング:
- 社内情報システム・ランタイム・アプリケーション・プログラム・インターフェース(API)を公開するには
- 実行時に動的実行プログラムのコマンドで文字列を含むへ
注釈は、外部ファイルに格納されているのではなく、直接ソースコードで記述されたメタデータ情報です。
注釈メタデータは、タグに取り付けられています。反射が取得するために、実行時にメタデータコードを注釈を付けて、実行前に動的プロキシオブジェクトコードすることができ、ダイナミックな動的エージェントを実行することによって達成----実際にAOPの核となるアイデアでビジネスロジックに注入しましたソースコードを変更せずに、技術は、動的に新しい機能のプログラムに加えました。
2.ノート
KotlinアノテーションはJavaアノテーションと完全に互換性があります。
2.1ステートメントノート
でKotlinにおける文のノートannotation class
キーワードは、例えば、我々は次のように実行TestCaseクラスを宣言し、2つの音符する必要があります。
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION
)
@Retention(AnnotationRetention.SOURCE)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String)
@Target(AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@Repeatable
@MustBeDocumented
annotation class Run
キーワードこのコメントから見ることができ、クラスで、コンパイラは、コンパイラで型チェックの種類にコメントすることができます。
メタアノテーションと呼ばれる使用注釈のカスタム注釈、。注釈クラスウェイにメタアノテーションを追加することによって、追加のプロパティを指定します。元のメモには、表で説明しました:
メタ注釈名 | 機能 |
---|---|
@目標 | クラスCLASS、注釈クラスANNOTATION_CLASS、汎用パラメータのTYPE_PARAMETER、機能FUNCTION、属性プロパティ、ドメインメンバを記述するために使用:この注釈は、それらが列挙されているクラスをkotlin.annotation.AnnotationTargetれ、これらの要素のために定義された要素(指定することができますフィールド変数は、ローカル変数は、LOCAL_VARIABLE VALUE_PARAMETER、コンストラクタPROPERTY_GETTER、PROPERTY_SETTER、タイプエイリアスTYPEALIASようなクラス、(注釈のタイプを含む)インターフェース、または列挙宣言型、表現式、ファイルのファイルを記述するために使用 |
@保持 | 指定は、注釈情報は、コンパイルされたクラスファイルに保存されているかどうか、ならびに、実行時の反射によってそれにアクセスするかどうかを、列挙された値は、好ましくは三つある:SOURCE(注釈データは、バイナリ出力に記憶されていません)バイナリ(コメントデータは、バイナリ出力に記憶されているが、反射が表示されていない)、ランタイム(バイナリ出力に記憶されたコメントデータは、反射のために使用することができる(デフォルト) |
@Repeatable | 単一の要素の注釈に複数回の使用が可能に |
@MustBeDocumented | 注釈が自動的に生成されたクラスや関数のシグネチャのAPIドキュメントで、パブリックAPIの一部であることを示し、この情報は注釈を含める必要があります。 |
2.2使用注釈
上記は、我々はそれがクラス、関数、VALUE_PARAMETERとEXPRESSION、私たちはクラスで使用ここに示した例で使用することができ、実行ノートを宣言します。
@Run
class SwordTest()
関数にこのアノテーションを使用するとき、我々はコンストラクタを宣言したテストケースのアノテーションは、ID文字列引数の型が渡されました。
@Run
class SwordTest() {
@TestCase(id = "a")
fun testCase(testId: String) {
println("Run SwordTest Id = $testId")
}
}
上記の例は、コードのコメントを使用するのは簡単です。前記@TestCaseは、(ID =「」)注釈コンストラクタを使用することです。
注釈は、コンストラクタの引数を持つことができます。次のようにサポートされる注釈パラメータのデータ型は、次のとおりです。
- 基本データ型
- ストリング
- KClass
- 列挙型
- 注記
- 上記で参照したの基本データ型の配列に加えて
次の2つのステートメントが採用されていません
annotation class TestCase1(val id: Array<Int>)
annotation class TestCase2(val id: SwordTest)
注釈属性の値が格納されているようJVMがnullをサポートしていないため、また、注釈タイプがnullを持つことができないことに注意してください。
別の注釈へのパラメータの注釈として使用する場合は、その名前は、@文字で始まることはできません。
例えば:
annotation class AnnoX(val value: String)
annotation class AnnoY(
val message: String
val annoX: AnnoX = AnnoX("X"))
2.3注釈プロセス
は対応するアノテーション情報を処理ロジックフローが存在しない場合、それは注釈廃棄物は、実用的な価値がないと言うことができます。
まず第一に、私たちの目標は、テストクラスにあります。
@Run
class SwordTest() {
@TestCase(id = "a")
fun testCase(testId: String) {
println("Run SwordTest Id = $testId")
}
}
私たちは、@TestCaseコメントの処理機能への作用を必要とします
::class
基準は、
第一の可変点SwordTestインスタンス宣言
、この変数使用して、オブジェクトのクラス情報を取得することができるが::class
クラスKClassを得るために、基準剣オブジェクト・インスタンスを
val sword = SwordTest()
val kClass = sword::class
//有点像Java中的 getClass()
//上面这行代码,Kotlin编译器会自动推断出 kClass变量的类型是
val kClass:KClass<out SwordTest> = sword::class
- メンバーの拡張属性は、
今、私たちは宣言されているすべての機能の剣オブジェクトの種類を取得する必要があります。Kotlinは、直接、すべての機能が、このクラスで宣言を取得するために拡張属性declaredFunctionsを使用することができます。
//返回的是一个 Collection<KCallable<*>> 其中*是Koltlin泛型中的星投影
val members = kClass.members
- 注釈のプロパティ
継承KCallable、KCallableのKFunctionタイプもKAnnotatedElementを継承しています。KAnnotatedElementありpublic val annotations:List
、すべてのノート情報の機能に保存されたプロパティが。リストのこの店の注釈を横断することにより、あなたはテストケースの注釈を取得することができます。
for (f in members ) {
f.annotations.forEach {
if(it is TestCase){
val id = it.id //TestCase 注解的属性ID
doSomething(id) //注解处理逻辑
}
}
}
- 関数呼び出しは
、あなたが反射によって機能を呼び出したい場合はさらに、()を呼び出し、直接使用することができます。
f.call(sword, id)
//等价于
f.javaMethod?.invoke(sword, id)
//到这里,我们就完成了一个简单的注解处理器,完整的代码如下:
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION
)
@Retention(AnnotationRetention.SOURCE)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String)
class SwordTest {
@TestCase(id = "a")
fun testCase(testId: String) {
println("Run SwordTest Id = $testId")
}
}
fun testAnnoProcessing() {
val sword = SwordTest()
val kClass = sword::class
val members = kClass.members
for (f in members) {
f.annotations.forEach {
if (it is TestCase) {
val id = it.id
doSomething(id)
f.call(sword, id)
}
}
}
}
private fun doSomething(id: String) {
println("Do Something in Annotation Processing $id ${System.currentTimeMillis()}")
}
//测试:
main(){
testAnnoProcessing()
}
3.反射
Kotlinでは、我々は、反射機能を達成するための2つの方法があります。
一つは、Java APIパッケージの反射を呼び出して
別のパケットを直接kotlin.reflect Kotlin言語は以下のAPIを提供して呼ばれています
しかし、我々はKotlinのリフレクションAPIを使用したい場合も、あるKotlinは下に別のkotlin-反映-1.1.xx.jarにkotlin.reflectパッケージを入れていないすべてのプログラミングシナリオは、使用される機能を反映したシナリオ、なぜなら依存関係を追加する予定。
3.1クラス参照
我々はコード例を定義します。
open class BaseContainer<T>
class Container<T : Comparable<T>> : BaseContainer<Int> {
val elements: MutableList<T>
constructor(elements: MutableList<T>) {
this.elements = elements
}
fun sort(): Container<T> {
elements.sort()
return this
}
override fun toString(): String {
return "Container(elements = $elements)"
}
}
クラス参照を取得する隕石で反射されます。我々はすでに使用があることを知っている::class
現在のオブジェクトKClassオブジェクトを取得することができます。
val container = Container(mutableListOf<Int>(1, 3, 2, 5, 4, 7, 6))
val kClass = container::class
//如果是要使用Java中的类引用,就要使用javaClass
val jClass = container.javaClass
//或者使用KClass实例的.java属性
val jkClass = kClass.java
3.2機能参考
例では、INTは関数が奇数であるかどうかを決定する整数簡単あります。
fun isOdd(x: Int) = x % 2 != 0
//代码中调用
>>>isOdd(4)
false
また、パラメータとして使用したい場合は高階関数を使用する::
演算子を
val nums = listOf(1, 2, 3)
val filteredNums = nums.filter(::isOdd)
println(filteredNums) //输出 [1,3]
ここでは::isOdd
関数型(intは)ある- >ブール値
3.3属性が引用
Kotlinに、第一段階に属するアクセス属性オブジェクトは、「::」演算子を使用することができます
var one = 1
fun testReflectProperty() {
println(::one.get())
::one.set(2)
println(one)
}
::表現の一つのタイプは、私たちができますプロパティKProperty、と等価であるget()
関数の値を取得します。
缶端プロパティVAR = 1一種類KMutablePropertyの戻り値、およびまた、設定()メソッド
3.3結合機能とプロパティ参照
val digitRegex = "\\d+".toRegex()
digitRegex.matches("a")
digitRegex.matches("4")
digitRegex.matches("1")
digitRegex.matches("O")
私たちは、定期的な定義DigitRegex.matchesは、比較のモデルに見えました。
Kotlinで直接オブジェクトインスタンスを参照することができるdigitRegex()メソッドと一致し、上記のコードは次のように書くことができます。
val digitRegex = "\\d+".toRegex()
val isDigit = digitRegex::matches
isDigit("a")
isDigit("4")
isDigit("1")
isDigit("O")
一般的な反射情報を取得します。4.
などの注釈クラス、メソッド、メンバ変数に反射することによって得ることができ、その後、あなたはそれを反映したジェネリック情報を介して取得することはできませんか?
私たちは、Javaがジェネリックが消去されたことを知って、プログラムの実行は、ジェネリックを取得することはできません。
親からこのクラスの継承は、親クラスがジェネリック情報を持っている場合、あなたは呼び出すことができgetGenericSuperclass()
、一般的な親クラスのメソッドに関する情報を取得します。
getGenericSuperclass()一般的な継承は特殊なケースです。この場合、サブクラスは親クラスのジェネリックパラメータの型が保存されます。ParameterizedTypeを返します。また、当社は、一般的なJavaバイトコードが消去されてい、消去は常にオブジェクトタイプではありませんでしたが、消去へのキャップの種類。
Kotlinでは一般的なメカニズムは同じですが、それは反射によって得ることができる唯一の一般的な親の一般的な情報のサブクラスにすることができます。
コードの特定の例としては以下の通りであります:
class A<T>
open class C<T>
class B<T> : C<Int>() //继承父类 C<Int>()
fun fooA() {
//无法在此处获得运行时T的具体类型 , 下面代码运行时会报错
val parameterizedType = A<Int>()::class.java.genericSuperclass as ParameterizedType
val actualTypeArguments = parameterizedType.actualTypeArguments
for (type in actualTypeArguments) {
val typeName = type.typeName
println("typeName = ${typeName}") //运行会报错
}
}
fun fooB() {
//当继承了 父类C<Int>的时候,在此处能够获得运行时genericSuperclass T的具体类型
val parameterizedType = B<Int>()::class.java.genericSuperclass as ParameterizedType
val actualTypeArguments = parameterizedType.actualTypeArguments
for (type in actualTypeArguments) {
val typeName = type.typeName
println("typeName = ${typeName}") //输出 typeName = java.lang.Integer
}
}
反射Kotlinは、汎用コードの基本的な情報を取得する方法を示すために、ここで簡単な例。
まず、私たちは、親クラスBaseContainerを宣言します。
open class BaseContainer<T>
その後、コンテナが親クラスを継承宣言
class Container<T : Comparable<T>> : BaseContainer<Int> {
val elements: MutableList<T>
constructor(elements: MutableList<T>) {
this.elements = elements
}
fun sort(): Container<T> {
elements.sort()
return this
}
override fun toString(): String {
return "Container(elements = $elements"
}
}
その後、我々はこのような情報を取得するために行ってきました:
//在声明一个Container对象实例
val container = Container(mutableListOf<Int>(1, 3, 2, 5, 4, 7, 6))
//然后获取container的KClass对象引用
val kClass = container::class
//KClass对象的typeParameters属性中存有类型参数的信息,代码示例如下:
val typeParameters = kClass.typeParameters//获取类型参数typeParameters,也叫泛型信息
//它是一个数组,我们获取第一个对象
val kTypeParameters = typeParameters[0]
その後、情報次kTypeParameters:
コンストラクタ属性コンストラクタがKClassコンストラクタなどから取得することができる基準情報に情報:
val constructors = kClass.constructors
for (KFunction in constructors) {
KFunction.parameters.forEach {
val name = it.name
val type = it.type
println("name = $name") //输出 elements
println("type = $type") //输出 kotlin.collections.MutableList<T>
for (KTypeProjection in type.arguments) {
println(KTypeProjection.type) //输出 : T
}
}
}