الفرق بين المراجعتين لصفحة: «Kotlin/annotations»
طلا ملخص تعديل |
تعديل مصطلح متحول |
||
سطر 35: | سطر 35: | ||
=== البواني (Constructors) === | === البواني (Constructors) === | ||
وقد يكون للتوصيفات بوانٍ تقبل | وقد يكون للتوصيفات بوانٍ تقبل المعاملات (parameters)، مثل:<syntaxhighlight lang="kotlin"> | ||
annotation class Special(val why: String) | annotation class Special(val why: String) | ||
@Special("example") class Foo {} | @Special("example") class Foo {} | ||
</syntaxhighlight>حيث يُسمَح بأنواع | </syntaxhighlight>حيث يُسمَح بأنواع المعاملات الآتية: | ||
* الأنواع الموافقة للأنواع الأساسيّة (primitive) في لغة Java (مثل int و Long و... وإلخ.) | * الأنواع الموافقة للأنواع الأساسيّة (primitive) في لغة Java (مثل int و Long و... وإلخ.) | ||
* السلاسل النصيّة (strings) | * السلاسل النصيّة (strings) | ||
سطر 48: | سطر 48: | ||
ولا يُسمَح أن تكون هذه الأنواع nullable لأن JVM لا تدعم تخزين القيمة <code>null</code> في خاصّة التوصيف (annotation attribute). | ولا يُسمَح أن تكون هذه الأنواع nullable لأن JVM لا تدعم تخزين القيمة <code>null</code> في خاصّة التوصيف (annotation attribute). | ||
وعند استخدام التوصيف | وعند استخدام التوصيف كمعاملٍ (parameter) لتوصيفٍ آخر فلا يُسبَق اسمه بالرمز <code>@</code> مثل:<syntaxhighlight lang="kotlin"> | ||
annotation class ReplaceWith(val expression: String) | annotation class ReplaceWith(val expression: String) | ||
سطر 56: | سطر 56: | ||
@Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other")) | @Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other")) | ||
</syntaxhighlight>وعند تحديد الصنف (class) | </syntaxhighlight>وعند تحديد الصنف (class) كوسيطٍ للتوصيف فيُستخدَم صنف Kotlin والمُسمّى <code>KClass</code> ، والذي يُحوِّله المُترجِم إلى صنف Java تلقائيًا بحيث تصبح شيفرة Java قادرةً على رؤية التوصيفات والوسائط بشكلٍ طبيعيّ، مثل:<syntaxhighlight lang="kotlin"> | ||
import kotlin.reflect.KClass | import kotlin.reflect.KClass | ||
سطر 72: | سطر 72: | ||
== أهداف مواقع استخدام التوصيفات (Annotation Use-site Targets) == | == أهداف مواقع استخدام التوصيفات (Annotation Use-site Targets) == | ||
عند توصيف إحدى الخاصّيّات (property) أو | عند توصيف إحدى الخاصّيّات (property) أو معاملات الباني الأساسي (primary constructor parameter) فهناك الكثير من عناصر Java المُولَّدة من عناصر Kotlin الموافقة لها، وبالتالي تتعدّد المواقع المُمكِنة لاستخدام التوصيف في شيفرة Java bytecode المُولَّدة، ولتحديد كيفيّة توليد التوصيف بشكلٍ دقيقٍ، تُستخدَم الصيغة الآتية:<syntaxhighlight lang="kotlin"> | ||
class Example(@field:Ann val foo, // توصيف حقل Java | class Example(@field:Ann val foo, // توصيف حقل Java | ||
@get:Ann val bar, // توصيف الوصول عبر getter | @get:Ann val bar, // توصيف الوصول عبر getter | ||
سطر 91: | سطر 91: | ||
* <code>get</code> (الحصول على الخاصّيّة عبر getter) | * <code>get</code> (الحصول على الخاصّيّة عبر getter) | ||
* <code>set</code> (ضبط الخاصّيّة عبر setter) | * <code>set</code> (ضبط الخاصّيّة عبر setter) | ||
* <code>receiver</code> ( | * <code>receiver</code> (معامل المستقبِل للدالة الإضافيّة [extension function] أو الخاصّيّة) | ||
* <code>param</code> ( | * <code>param</code> (معامل الباني [constructor]) | ||
* <code>setparam</code> ( | * <code>setparam</code> (معامل ضبط الخاصّيّة عبر setter) | ||
* <code>delegate</code> (حقل تخزين نسخة التعميم (delegate instance) من الخاصّيّات المُعمَّمة) | * <code>delegate</code> (حقل تخزين نسخة التعميم (delegate instance) من الخاصّيّات المُعمَّمة) | ||
ولتوصيف | ولتوصيف معامل المستقبِل (receiver parameter) في الدالة الإضافيّة (extension function) تُستخدَم الصيغة الآتية:<syntaxhighlight lang="kotlin"> | ||
fun @receiver:Fancy String.myExtension() { } | fun @receiver:Fancy String.myExtension() { } | ||
</syntaxhighlight>وإن لم يُحدَّد هدف موقع الاستخدام (use-site target) فسيصبح الهدف بحسب توصيف <code>@Target</code> من التوصيف المُستخدَم، أمّا إن كان هناك أكثرُ من هدفٍ محتملٍ، فسيتم اعتماد أولّ عنصرٍ ممكنٍ من هذه القائمة: | </syntaxhighlight>وإن لم يُحدَّد هدف موقع الاستخدام (use-site target) فسيصبح الهدف بحسب توصيف <code>@Target</code> من التوصيف المُستخدَم، أمّا إن كان هناك أكثرُ من هدفٍ محتملٍ، فسيتم اعتماد أولّ عنصرٍ ممكنٍ من هذه القائمة: | ||
سطر 119: | سطر 119: | ||
} | } | ||
} | } | ||
</syntaxhighlight>إذ لا يمكن استخدام الصيغة الاعتياديّة لاستدعاء الدالة لتمرير | </syntaxhighlight>إذ لا يمكن استخدام الصيغة الاعتياديّة لاستدعاء الدالة لتمرير الوسائط (arguments passing) لأن ترتيب الوسائط في التوصيف المكتوب بلغة Java غير معرَّف، بل تُستخدَم بدلًا عن ذلك صيغة الوسائط المُسمّاة كما في الشيفرة:<syntaxhighlight lang="java"> | ||
// Java شيفرة | // Java شيفرة | ||
public @interface Ann { | public @interface Ann { | ||
سطر 128: | سطر 128: | ||
// Kotlin شيفرة | // Kotlin شيفرة | ||
@Ann(intValue = 1, stringValue = "abc") class C | @Ann(intValue = 1, stringValue = "abc") class C | ||
</syntaxhighlight>وكما هو الحال في لغة Java فإن | </syntaxhighlight>وكما هو الحال في لغة Java فإن المعامل <code>value</code> هو حالةٌ خاصةٌ لأن قيمته تُحدَّد بدون استخدام اسمٍ صريحٍ (explicit)، كما في الشيفرة:<syntaxhighlight lang="java"> | ||
// Java شيفرة | // Java شيفرة | ||
public @interface AnnWithValue { | public @interface AnnWithValue { | ||
سطر 138: | سطر 138: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== استخدام المصفوفات | === استخدام المصفوفات كمعاملات للتوصيف === | ||
إن كان | إن كان الوسيط<code>value</code> في لغة Java مصفوفةً فسيصبح معاملًا من النوع <code>vararg</code> في لغة Kotlin، مثل:<syntaxhighlight lang="java"> | ||
// Java شيفرة | // Java شيفرة | ||
public @interface AnnWithArrayValue { | public @interface AnnWithArrayValue { | ||
سطر 147: | سطر 147: | ||
// Kotlin شيفرة | // Kotlin شيفرة | ||
@AnnWithArrayValue("abc", "foo", "bar") class C | @AnnWithArrayValue("abc", "foo", "bar") class C | ||
</syntaxhighlight>أمّا إن كانت | </syntaxhighlight>أمّا إن كانت الوسائط الأخرى على شكل مصفوفاتٍ فيجب استخدام صيغة القيم المباشرة (literals) للمصفوفة (بدءًا من الإصدار Kotlin 1.2 ) أو استخدام <code>arrayOf(...)</code>، مثل:<syntaxhighlight lang="java"> | ||
// Java شيفرة | // Java شيفرة | ||
public @interface AnnWithArrayMethod { | public @interface AnnWithArrayMethod { |
مراجعة 17:47، 4 يوليو 2018
التصريح عن التوصيف (Annotation Declaration)
تُعدُّ التوصيفات إحدى الوسائل لإضافة بياناتٍ توصيفيّةٍ (metadata) إلى الشيفرة، وللتصريح عن التوصيف يُضاف المُحدِّد annotation
قبل اسم الصنف، مثل:
annotation class Fancy
وقد تُحدَّد بعض خواصّ التوصيفات (annotation attributes) باستخدام التوصيفات الآتية (meta-annotations) لتوصيفات الصنف:
@Target
لتحديد نوع العناصر التي يمكن توصيفها مثل الأصناف (classes) والدوال (functions) والخاصّيّات (properties) والتعابير (expressions) و... إلخ.-
@Retention
لتحديد فيما إن كان التوصيف مُخزَّنًا في ملفات الأصناف المُترجَمة، أو مرئيًا عبر انعكاسٍ (reflection) أثناء التنفيذ (runtime) (وكلاهما محقُّق بالحالة الافتراضية) -
@Repeatable
للسماح باستخدام نفس التوصيف على كائن واحدٍ لعدّة مرات -
@MustBeDocumented
لتحديد التوصيف كجزءٍ من الواجهة العامّة (public API) يجب تضمينه في الصنف (class) أو ترويسة التابع (method signature) الموجودَين في التوثيق المُولَّد للواجهة API
مثال:
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Fancy
الاستخدام (Usage)
ليكن الصنف Foo الآتي:
@Fancy class Foo {
@Fancy fun baz(@Fancy foo: Int): Int {
return (@Fancy 1)
}
}
لتوصيف الباني الأساسيّ (primary constructor) للصنف تجب إضافة الكلمة المفتاحيّة constructor
إلى تصريحه مسبوقةً بالتوصيف كما في الشيفرة:
class Foo @Inject constructor(dependency: MyDependency) {
// ...
}
كما ويمكن توصيف الوصول (accessing) إلى الخاصّيّات (get أو set) كما في الشيفرة:
class Foo {
var x: MyDependency? = null
@Inject set
}
البواني (Constructors)
وقد يكون للتوصيفات بوانٍ تقبل المعاملات (parameters)، مثل:
annotation class Special(val why: String)
@Special("example") class Foo {}
حيث يُسمَح بأنواع المعاملات الآتية:
- الأنواع الموافقة للأنواع الأساسيّة (primitive) في لغة Java (مثل int و Long و... وإلخ.)
- السلاسل النصيّة (strings)
- الأصناف (classes) (بالصيغة
Foo::class
) - الثوابت المتعدِّدة (enums)
- التوصيفات الأخرى
- مصفوفة (array) من أيّ من الأنواع السابقة
ولا يُسمَح أن تكون هذه الأنواع nullable لأن JVM لا تدعم تخزين القيمة null
في خاصّة التوصيف (annotation attribute).
وعند استخدام التوصيف كمعاملٍ (parameter) لتوصيفٍ آخر فلا يُسبَق اسمه بالرمز @
مثل:
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"))
وعند تحديد الصنف (class) كوسيطٍ للتوصيف فيُستخدَم صنف Kotlin والمُسمّى KClass
، والذي يُحوِّله المُترجِم إلى صنف Java تلقائيًا بحيث تصبح شيفرة Java قادرةً على رؤية التوصيفات والوسائط بشكلٍ طبيعيّ، مثل:
import kotlin.reflect.KClass
annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any>)
@Ann(String::class, Int::class) class MyClass
تعابير Lambdas
تُضاف التوصيفات لتعابير lambdas عبر تطبيقها على التابع inoke()
الذي من خلاله ستُولَّد بُنية lambda، وهذا يُفيد في بيئات العمل (frameworks ) مثل Quasar التي تستخدِم التوصيفات للتحكم بالتزامن (concurrency)، مثل:
annotation class Suspendable
val f = @Suspendable { Fiber.sleep(10) }
أهداف مواقع استخدام التوصيفات (Annotation Use-site Targets)
عند توصيف إحدى الخاصّيّات (property) أو معاملات الباني الأساسي (primary constructor parameter) فهناك الكثير من عناصر Java المُولَّدة من عناصر Kotlin الموافقة لها، وبالتالي تتعدّد المواقع المُمكِنة لاستخدام التوصيف في شيفرة Java bytecode المُولَّدة، ولتحديد كيفيّة توليد التوصيف بشكلٍ دقيقٍ، تُستخدَم الصيغة الآتية:
class Example(@field:Ann val foo, // توصيف حقل Java
@get:Ann val bar, // توصيف الوصول عبر getter
@param:Ann val quux) // توصيف باني أساسي
وبالإمكان استخدام نفس الصيغة السابقة لتوصيف كامل الملفّ، إذ يُوضَع التوصيف بالهدف file
في المستوى الأعلى (top-level) من الملف قبل مُوجِّه الحزمة (package directive) أو قبل أيٍّ من عمليات الاستيراد (import) إن كان الملف واقعًا في الحزمة الافتراضية، مثل:
@file:JvmName("Foo")
package org.jetbrains.demo
وعند وجود أكثر من توصيفٍ بنفس الهدف تُضاف الأقواس []
بعد الهدف مباشرةً ويُوضع كامل التوصيف داخلها وذلك لمنع التكرار ، مثل:
class Example {
@set:[Inject VisibleForTesting]
var collaborator: Collaborator
}
تدعم لغة Kotlin القائمة الآتية من الأهداف:
file
property
(توصيف هذا الهدف غير مرئيٍّ بالنسبة للغة Java)field
get
(الحصول على الخاصّيّة عبر getter)set
(ضبط الخاصّيّة عبر setter)receiver
(معامل المستقبِل للدالة الإضافيّة [extension function] أو الخاصّيّة)param
(معامل الباني [constructor])setparam
(معامل ضبط الخاصّيّة عبر setter)delegate
(حقل تخزين نسخة التعميم (delegate instance) من الخاصّيّات المُعمَّمة)
ولتوصيف معامل المستقبِل (receiver parameter) في الدالة الإضافيّة (extension function) تُستخدَم الصيغة الآتية:
fun @receiver:Fancy String.myExtension() { }
وإن لم يُحدَّد هدف موقع الاستخدام (use-site target) فسيصبح الهدف بحسب توصيف @Target
من التوصيف المُستخدَم، أمّا إن كان هناك أكثرُ من هدفٍ محتملٍ، فسيتم اعتماد أولّ عنصرٍ ممكنٍ من هذه القائمة:
aram
property
field
التوصيفات في لغة Java
تتوافق التوصيفات في لغة Java بشكل تامّ مع لغة Kotlin، مثل:
import org.junit.Test
import org.junit.Assert.*
import org.junit.Rule
import org.junit.rules.*
class Tests {
// تطبيق التوصيف @Rule
// على الوصول للخاصية عبر getter
@get:Rule val tempFolder = TemporaryFolder()
@Test fun simple() {
val f = tempFolder.newFile()
assertEquals(42, getTheAnswer())
}
}
إذ لا يمكن استخدام الصيغة الاعتياديّة لاستدعاء الدالة لتمرير الوسائط (arguments passing) لأن ترتيب الوسائط في التوصيف المكتوب بلغة Java غير معرَّف، بل تُستخدَم بدلًا عن ذلك صيغة الوسائط المُسمّاة كما في الشيفرة:
// Java شيفرة
public @interface Ann {
int intValue();
String stringValue();
}
وفي لغة Kotlin بالشكل:
// Kotlin شيفرة
@Ann(intValue = 1, stringValue = "abc") class C
وكما هو الحال في لغة Java فإن المعامل value
هو حالةٌ خاصةٌ لأن قيمته تُحدَّد بدون استخدام اسمٍ صريحٍ (explicit)، كما في الشيفرة:
// Java شيفرة
public @interface AnnWithValue {
String value();
}
وفي لغة Kotlin:
// Kotlin شيفرة
@AnnWithValue("abc") class C
استخدام المصفوفات كمعاملات للتوصيف
إن كان الوسيطvalue
في لغة Java مصفوفةً فسيصبح معاملًا من النوع vararg
في لغة Kotlin، مثل:
// Java شيفرة
public @interface AnnWithArrayValue {
String[] value();
}
وفي لغة Kotlin تصبح الشيفرة:
// Kotlin شيفرة
@AnnWithArrayValue("abc", "foo", "bar") class C
أمّا إن كانت الوسائط الأخرى على شكل مصفوفاتٍ فيجب استخدام صيغة القيم المباشرة (literals) للمصفوفة (بدءًا من الإصدار Kotlin 1.2 ) أو استخدام arrayOf(...)
، مثل:
// Java شيفرة
public @interface AnnWithArrayMethod {
String[] names();
}
وتصبح في لغة Kotlin:
// Kotlin 1.2 بدءًا من الإصدار
@AnnWithArrayMethod(names = ["abc", "foo", "bar"])
class C
// الإصدارات الأقدم
@AnnWithArrayMethod(names = arrayOf("abc", "foo", "bar"))
class D
الوصول إلى الخاصّيّات (Properties) عبر نسخة التوصيف (Annotation Instance)
تظهر قيم نسخة التوصيف (annotation instance) وكأنها خاصّيّات في شيفرة Kotlin كما يلي:
// Java شيفرة
public @interface Ann {
int value();
}
وتصبح في لغة Kotlin:
// Kotlin شيفرة
fun foo(ann: Ann) {
val i = ann.value
}