الفرق بين المراجعتين ل"Kotlin/delegated properties"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(أنشأ الصفحة ب'<noinclude>{{DISPLAYTITLE:الخاصّيّات المُعمَّمة (Delegated Properties) في لغة Kotlin}}</noinclude> == استخدام الخاصّيّات الم...')
 
(أمور تنسيقيّة)
سطر 5: سطر 5:
 
* الخاصّيّات المُراقَبة (observable property): إذ يُستدعَى مسؤول الانتظار (listener) عند حدوث أي تغييرٍ في الخاصّيّة.
 
* الخاصّيّات المُراقَبة (observable property): إذ يُستدعَى مسؤول الانتظار (listener) عند حدوث أي تغييرٍ في الخاصّيّة.
 
* تخزين الخاصّيّات في map بدلًا من حقلٍ منفصلٍ لكلِّ منها.
 
* تخزين الخاصّيّات في map بدلًا من حقلٍ منفصلٍ لكلِّ منها.
وتشمل لغة Kotlin كلّ تلك الحالات بدعمها للخاصّيّات المُعمَّمة (delegated properties) بالصيغة العامّة الآتية:
+
وتشمل لغة Kotlin كلّ تلك الحالات بدعمها للخاصّيّات المُعمَّمة (delegated properties) بالصيغة العامّة الآتية:<syntaxhighlight lang="kotlin">
 
+
val/var <property name>: <Type> by <expression>
<code>val/var <property name>: <Type> by <expression></code>
+
</syntaxhighlight>كما في الشيفرة الآتية:<syntaxhighlight lang="kotlin">
 
 
كما في الشيفرة الآتية:<syntaxhighlight lang="kotlin">
 
 
class Example {
 
class Example {
 
     var p: String by Delegate()
 
     var p: String by Delegate()
سطر 26: سطر 24:
 
val e = Example()
 
val e = Example()
 
println(e.p)
 
println(e.p)
</syntaxhighlight>وهذا سيُظهر النتيجة:
+
</syntaxhighlight>وهذا سيُظهر النتيجة:<syntaxhighlight>
 
+
Example@33a17727, thank you for delegating ‘p’ to me!
<code>Example@33a17727, thank you for delegating ‘p’ to me!</code>
+
</syntaxhighlight>وبشكلٍ مشابهٍ؛ تُستدعَى الدالة <code>setValue()‎</code> عند الإسناد للمتحول <code>p</code>، حيث يتماثل المتحولان الأول والثاني بينما يحتوي الثالث على القيمة المُسندَة، فعند تنفيذ الشيفرة الآتية:<syntaxhighlight lang="kotlin">
 
 
وبشكلٍ مشابهٍ؛ تُستدعَى الدالة <code>setValue()‎</code> عند الإسناد للمتحول <code>p</code>، حيث يتماثل المتحولان الأول والثاني بينما يحتوي الثالث على القيمة المُسندَة، فعند تنفيذ الشيفرة الآتية:<syntaxhighlight lang="kotlin">
 
 
e.p = "NEW"
 
e.p = "NEW"
</syntaxhighlight>ستظهر النتيجة:
+
</syntaxhighlight>ستظهر النتيجة:<syntaxhighlight>
 
+
NEW has been assigned to ‘p’ in Example@33a17727.
<code>NEW has been assigned to ‘p’ in Example@33a17727.</code>
+
</syntaxhighlight>وسيأتي شرح متطلَّبات القيام بمثل هذا التعميم في فقرةٍ لاحقةٍ في الصفحة الحاليّة.
 
 
وسيأتي شرح متطلَّبات القيام بمثل هذا التعميم في فقرةٍ لاحقةٍ في الصفحة الحاليّة.
 
  
 
'''ملاحظة:''' أصبح بالإمكان بدءًا من الإصدار Kotlin 1.1 التصريحُ عن الخاصّيَة المُعمَّمة داخل الدالة أو جزءٍ من الشيفرة (code block) ولا يُشترَط أن تكون عنصرًا (member) في الصنف، وستجد مثالًا عنها تحت عنوان الخاصيات المُعمَّمة المحليّة في هذه الصفحة.
 
'''ملاحظة:''' أصبح بالإمكان بدءًا من الإصدار Kotlin 1.1 التصريحُ عن الخاصّيَة المُعمَّمة داخل الدالة أو جزءٍ من الشيفرة (code block) ولا يُشترَط أن تكون عنصرًا (member) في الصنف، وستجد مثالًا عنها تحت عنوان الخاصيات المُعمَّمة المحليّة في هذه الصفحة.
سطر 54: سطر 48:
 
     println(lazyValue)
 
     println(lazyValue)
 
}
 
}
</syntaxhighlight>سينتُج عن تنفيذ الشيفرة السابقة النتيجة:
+
</syntaxhighlight>سينتُج عن تنفيذ الشيفرة السابقة النتيجة:<syntaxhighlight>
 +
computed!
  
<code>computed!</code>
+
Hello
  
<code>Hello</code>
+
Hello
  
<code>Hello</code>
 
  
وتُعدُّ عملية حساب الخاصّيّة الكسولة متزامنةً (synchronized) حيث تُحسَب في thread واحدٍ وستحصل كل threads الأخرى نفس القيمة، وإن لم يكن مهمًا أن تكون عملية التهيئة متزامنةً سيُسمح القيام بها بأكثر من thread بنفس الوقت وحينئذٍ يجب تمرير <code>LazyThreadSafetyMode.PUBLICATION</code> للمتحوّل الأول في الدالة <code>lazy()‎</code> إذ يُستخدَم النمط <code>LazyThreadSafetyMode.NONE</code> عندما يُضمَن أنّ عملية التهيئة لن تحدث بأكثر من thread.
+
</syntaxhighlight>وتُعدُّ عملية حساب الخاصّيّة الكسولة متزامنةً (synchronized) حيث تُحسَب في thread واحدٍ وستحصل كل threads الأخرى نفس القيمة، وإن لم يكن مهمًا أن تكون عملية التهيئة متزامنةً سيُسمح القيام بها بأكثر من thread بنفس الوقت وحينئذٍ يجب تمرير <code>LazyThreadSafetyMode.PUBLICATION</code> للمتحوّل الأول في الدالة <code>lazy()‎</code> إذ يُستخدَم النمط <code>LazyThreadSafetyMode.NONE</code> عندما يُضمَن أنّ عملية التهيئة لن تحدث بأكثر من thread.
  
 
=== المُراقَبة (Observable) ===
 
=== المُراقَبة (Observable) ===
سطر 80: سطر 74:
 
     user.name = "second"
 
     user.name = "second"
 
}
 
}
</syntaxhighlight>والتي سينتُج عنها:
+
</syntaxhighlight>والتي سينتُج عنها:<syntaxhighlight>
 
+
<no name> -> first
<code><no name> -> first</code>
+
first -> second
 
+
</syntaxhighlight>ويُستفادُ في بعض الأحيان من الدالة <code>()vetoable</code> بدلًا من <code>observable()‎</code> ، حيث سيُستدعَى المسؤول (handler) المُمرَّر للدالة <code>()vetoable</code> '''قبل''' تنفيذ عملية الإسناد للخاصّيّة.
<code>first -> second</code>
 
 
 
ويُستفادُ في بعض الأحيان من الدالة <code>()vetoable</code> بدلًا من <code>observable()‎</code> ، حيث سيُستدعَى المسؤول (handler) المُمرَّر للدالة <code>()vetoable</code> '''قبل''' تنفيذ عملية الإسناد للخاصّيّة.
 
  
 
== تخزين الخاصيات في Map ==
 
== تخزين الخاصيات في Map ==

مراجعة 05:23، 15 مارس 2018

استخدام الخاصّيّات المُعمَّمة

تستطيع في لغة Kotlin تعريف استخدام (implement) الخاصّيّات يدويًا مرارًا وتكرارًا بكل مرةٍ تحتاجها، ولكن من الأسهل تعريف استخدامها مرةً واحدةً وتخزين هذا التعريف في المكتبة (library) للاستفادة منه كلما دعت الحاجة، وهذا يشمل:

  • الخاصّيّات الكسولة (Lazy property): تُحسب قيمتها مرةً واحدةً فقط وذلك عند الوصول إليها للمرّة الأولى.
  • الخاصّيّات المُراقَبة (observable property): إذ يُستدعَى مسؤول الانتظار (listener) عند حدوث أي تغييرٍ في الخاصّيّة.
  • تخزين الخاصّيّات في map بدلًا من حقلٍ منفصلٍ لكلِّ منها.

وتشمل لغة Kotlin كلّ تلك الحالات بدعمها للخاصّيّات المُعمَّمة (delegated properties) بالصيغة العامّة الآتية:

val/var <property name>: <Type> by <expression>

كما في الشيفرة الآتية:

class Example {
    var p: String by Delegate()
}

إذ إن التعبير الواقع بعد الكلمة المفتاحيّة by سيكون هو المُعمَّم (delegate) لأن دالتيّ get()‎ و set()‎ ستُعمَّمان للدالتين getValue()‎ و setValue()‎ الموافقتين لهما، وبالتالي لا حاجة لتعريف استخدام (implement) أيّ واجهةٍ، بل يكفي وجود الدالة getValue()‎ (والدالة setValue()‎ في حالة المتحولات من النوع var)، مثل الشيفرة الآتية:

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }
 
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

فعند قراءة القيمة من p والمُعمَّمة لأي كائن (instance) من Delegate تُستدعَى الدالة getValue()‎ من Delegate حيث يكون المتحول (parameter) الأوّل فيها هو الكائن الذي تٌقرَأ منه قيمة p ويحتوي الثاني على وصفٍ لها (للحصول على اسمها مثلًا)، مثل:

val e = Example()
println(e.p)

وهذا سيُظهر النتيجة:

Example@33a17727, thank you for delegating ‘p’ to me!

وبشكلٍ مشابهٍ؛ تُستدعَى الدالة setValue()‎ عند الإسناد للمتحول p، حيث يتماثل المتحولان الأول والثاني بينما يحتوي الثالث على القيمة المُسندَة، فعند تنفيذ الشيفرة الآتية:

e.p = "NEW"

ستظهر النتيجة:

NEW has been assigned to ‘p’ in Example@33a17727.

وسيأتي شرح متطلَّبات القيام بمثل هذا التعميم في فقرةٍ لاحقةٍ في الصفحة الحاليّة.

ملاحظة: أصبح بالإمكان بدءًا من الإصدار Kotlin 1.1 التصريحُ عن الخاصّيَة المُعمَّمة داخل الدالة أو جزءٍ من الشيفرة (code block) ولا يُشترَط أن تكون عنصرًا (member) في الصنف، وستجد مثالًا عنها تحت عنوان الخاصيات المُعمَّمة المحليّة في هذه الصفحة.

التعميمات القياسيّة (Standard Delegates)

تحتوي مكتبة Kotlin القياسيّة على عددٍ من التوابع المُنتِجة (factory methods) لأنواع التعميمات المختلفة، وهي:

الكسولة (Lazy)

تأخذ الدالة lazy()‎ تعبير lambda وتعيد كائنًا (instance) من النوع Lazy<T>‎ المُعمَّم لتعريف استخدام الخاصية الكسولة، إذ تُحسَب قيمة تعبير lambda المُمرَّر للدالة عند الاستدعاء الأول (أي عند استخدام get()‎) لتُستخدَم نفسُ القيمة لكافّة الاستدعاءات اللاحقة، مثل:

val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

fun main(args: Array<String>) {
    println(lazyValue)
    println(lazyValue)
}

سينتُج عن تنفيذ الشيفرة السابقة النتيجة:

computed!

Hello

Hello

وتُعدُّ عملية حساب الخاصّيّة الكسولة متزامنةً (synchronized) حيث تُحسَب في thread واحدٍ وستحصل كل threads الأخرى نفس القيمة، وإن لم يكن مهمًا أن تكون عملية التهيئة متزامنةً سيُسمح القيام بها بأكثر من thread بنفس الوقت وحينئذٍ يجب تمرير LazyThreadSafetyMode.PUBLICATION للمتحوّل الأول في الدالة lazy()‎ إذ يُستخدَم النمط LazyThreadSafetyMode.NONE عندما يُضمَن أنّ عملية التهيئة لن تحدث بأكثر من thread.

المُراقَبة (Observable)

تحتوي الدالة Delegates.observable()‎ على متحولين (arguments) وهما: القيمة الأوليّة (initial value) ومسؤول (handler) التعديلات، إذ يُستدعَى المتحول الثاني بكلّ مرّةٍ يجري فيها إسنادٌ للخاصّيّة (بعد عمليّة الإسناد لا قبلها)، وتحتوي الدالة في بُنيتها (body) على ثلاثة متحولاتٍ: الخاصّيّة التي تُسنَد القيمة إليها والقيمة السابقة والقيمة الجديدة، كما في الشيفرة الآتية:

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main(args: Array<String>) {
    val user = User()
    user.name = "first"
    user.name = "second"
}

والتي سينتُج عنها:

<no name> -> first
first -> second

ويُستفادُ في بعض الأحيان من الدالة ()vetoable بدلًا من observable()‎ ، حيث سيُستدعَى المسؤول (handler) المُمرَّر للدالة ()vetoable قبل تنفيذ عملية الإسناد للخاصّيّة.

تخزين الخاصيات في Map

الخاصيات المُعمَّمة المحليّة (بدءًا من الإصدار 1.1)

متطلبات تعميم الخاصّيّات

قواعد الترجمة

التزويد بالتعميم (delegate) (بدءًا من الإصدار 1.1)

مصادر