عشر ميزات Kotlin لتعزيز تطوير Android

نشرت: 2022-03-11

مقدمة

منذ فترة ، قدم Tomasz تطوير Kotlin على Android. لتذكيرك: Kotlin هي لغة برمجة جديدة تم تطويرها بواسطة Jetbrains ، الشركة التي تقف وراء أحد أشهر IDEs Java ، IntelliJ IDEA. مثل Java ، تعد Kotlin لغة عامة الغرض. نظرًا لأنه يتوافق مع كود Java Virtual Machine (JVM) ، يمكن استخدامه جنبًا إلى جنب مع Java ، ولا يأتي مع أداء إضافي.

في هذه المقالة ، سأغطي أهم 10 ميزات مفيدة لتعزيز تطوير Android الخاص بك.

ملاحظة : في وقت كتابة هذا المقال ، كانت الإصدارات الفعلية هي Android Studio 2.1.1. وكوتلن 1.0.2.

كوتلن

هل سئمت من كود جافا الذي لا ينتهي؟ جرب Kotlin ووفر وقتك وعقلك.
سقسقة

إعداد Kotlin

نظرًا لأن Kotlin تم تطويره بواسطة JetBrains ، فهو مدعوم جيدًا في كل من Android Studio و IntelliJ.

الخطوة الأولى هي تثبيت ملحق Kotlin. بعد القيام بذلك بنجاح ، ستتوفر إجراءات جديدة لتحويل Java إلى Kotlin. هناك خياران جديدان هما:

  1. قم بإنشاء مشروع Android جديد وقم بإعداد Kotlin في المشروع.
  2. أضف دعم Kotlin إلى مشروع Android موجود.

لمعرفة كيفية إنشاء مشروع Android جديد ، راجع الدليل الرسمي خطوة بخطوة. لإضافة دعم Kotlin إلى مشروع تم إنشاؤه حديثًا أو مشروع قائم ، افتح مربع حوار البحث عن الإجراء باستخدام Command + Shift + A على Mac أو Ctrl + Shift + A على نظامي التشغيل Windows / Linux ، واستدعاء Configure Kotlin in Project .

لإنشاء فصل دراسي جديد في Kotlin ، حدد:

  • File > New > Kotlin file/class ، أو
  • File > New > Kotlin activity

بدلاً من ذلك ، يمكنك إنشاء فئة Java وتحويلها إلى Kotlin باستخدام الإجراء المذكور أعلاه. تذكر أنه يمكنك استخدامه لتحويل أي فئة أو واجهة أو تعداد أو تعليق توضيحي ، ويمكن استخدامه لمقارنة Java بسهولة برمز Kotlin.

من العناصر المفيدة الأخرى التي توفر الكثير من الكتابة هي امتدادات Kotlin. لاستخدامها ، يجب عليك تطبيق مكون إضافي آخر في ملف build.gradle للوحدة النمطية الخاصة بك:

 apply plugin: 'kotlin-android-extensions'

تحذير : إذا كنت تستخدم إجراء ملحق Kotlin لإعداد مشروعك ، فسيضع الكود التالي في ملف build.gradle المستوى الأعلى:

 buildscript { ext.kotlin_version = '1.0.2' repositories { jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }

سيؤدي ذلك إلى عدم عمل الامتداد. لإصلاح ذلك ، ما عليك سوى نسخ هذا الرمز إلى كل وحدة من وحدات المشروع التي ترغب في استخدام Kotlin فيها.

إذا قمت بإعداد كل شيء بشكل صحيح ، فيجب أن تكون قادرًا على تشغيل واختبار تطبيقك بنفس الطريقة التي تستخدمها في مشروع Android القياسي ، ولكن الآن تستخدم Kotlin.

توفير الوقت مع Kotlin

لذلك ، لنبدأ بوصف بعض الجوانب الرئيسية للغة Kotlin ومن خلال تقديم نصائح حول كيفية توفير الوقت باستخدامها بدلاً من Java.

الميزة رقم 1: استيراد التخطيط الثابت

أحد أكثر الأكواد المعيارية شيوعًا في Android هو استخدام وظيفة findViewById() للحصول على مراجع لآرائك في الأنشطة أو الأجزاء.

توجد حلول ، مثل مكتبة Butterknife ، التي تحفظ بعض الكتابة ، لكن Kotlin تتخذ هذه الخطوة الأخرى من خلال السماح لك باستيراد جميع المراجع إلى طرق العرض من التخطيط باستيراد واحد.

على سبيل المثال ، ضع في اعتبارك تخطيط XML للنشاط التالي:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="co.ikust.kotlintest.MainActivity"> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>

ورمز النشاط المصاحب:

 package co.ikust.kotlintest import android.support.v7.app.AppCompatActivity import android.os.Bundle import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) helloWorldTextView.text = "Hello World!" } }

للحصول على المراجع لجميع طرق العرض في التخطيط بمعرف محدد ، استخدم ملحق Android Kotlin Anko. تذكر أن تكتب بيان الاستيراد هذا:

 import kotlinx.android.synthetic.main.activity_main.*

لاحظ أنك لست بحاجة إلى كتابة فاصلة منقوطة في نهاية السطور في Kotlin لأنها اختيارية.

يتم استيراد TextView من التخطيط TextView بالاسم الذي يساوي معرف طريقة العرض. لا تخلط بين بناء الجملة المستخدم لتعيين التسمية:

 helloWorldTextView.text = "Hello World!"

سوف نغطي ذلك قريبا.

المحاذير :

  • تأكد من استيراد التخطيط الصحيح ، وإلا فإن مراجع العرض المستوردة ستحتوي على قيمة null .
  • عند استخدام الأجزاء ، تأكد من استخدام مراجع العرض التي تم استيرادها بعد استدعاء دالة onCreateView() . قم باستيراد التخطيط في وظيفة onCreateView() واستخدم مراجع العرض لإعداد واجهة المستخدم في onViewCreated() . لن يتم تعيين المراجع قبل انتهاء أسلوب onCreateView() .

الميزة رقم 2: كتابة فصول POJO باستخدام Kotlin

الشيء الذي سيوفر معظم الوقت مع Kotlin هو كتابة فئات POJO (كائن Java قديم عادي) المستخدمة للاحتفاظ بالبيانات. على سبيل المثال ، في نص الطلب والاستجابة لواجهة برمجة تطبيقات RESTful. في التطبيقات التي تعتمد على RESTful API ، سيكون هناك العديد من الفئات من هذا القبيل.

في Kotlin ، يتم عمل الكثير من أجلك ، وبناء الجملة موجز. على سبيل المثال ، ضع في اعتبارك الفئة التالية في Java:

 public class User { private String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }

عند العمل مع Kotlin ، لن تضطر إلى كتابة الكلمات الرئيسية العامة مرة أخرى. بشكل افتراضي ، كل شيء في نطاق عام. على سبيل المثال ، إذا كنت تريد الإعلان عن فصل دراسي ، فأنت تكتب ببساطة:

 class MyClass { }

ما يعادل كود Java أعلاه في Kotlin:

 class User { var firstName: String? = null var lastName: String? = null }

حسنًا ، هذا يوفر الكثير من الكتابة ، أليس كذلك؟ دعنا نتصفح كود Kotlin.

يوفر Kotlin الكثير من الكتابة

عند تحديد المتغيرات في Kotlin ، هناك خياران:

  • المتغيرات المتغيرة ، المحددة بواسطة var الكلمة.
  • المتغيرات غير القابلة للتغيير ، المحددة بواسطة كلمة val .

الشيء التالي الذي يجب ملاحظته هو أن بناء الجملة يختلف قليلاً عن Java ؛ أولاً ، تقوم بتعريف اسم المتغير ثم تتبعه بالنوع. أيضًا ، بشكل افتراضي ، الخصائص هي أنواع غير خالية ، مما يعني أنها لا تقبل قيمة null . لتعريف متغير لقبول قيمة null ، يجب إضافة علامة استفهام بعد النوع. سنتحدث عن هذا و null-safety في Kotlin لاحقًا.

شيء آخر مهم يجب ملاحظته هو أن Kotlin ليس لديها القدرة على إعلان الحقول للفصل الدراسي ؛ يمكن تحديد الخصائص فقط. لذلك ، في هذه الحالة ، firstName lastName عبارة عن خصائص تم تعيينها بطريقة افتراضية getter / setter. كما ذكرنا ، في Kotlin ، كلاهما عام بشكل افتراضي.

يمكن كتابة أدوات الوصول المخصصة ، على سبيل المثال:

 class User { var firstName: String? = null var lastName: String? = null val fullName: String? get() firstName + " " + lastName }

من الخارج ، عندما يتعلق الأمر بالصياغة ، تتصرف الخصائص مثل الحقول العامة في Java:

 val userName = user.firstName user.firstName = "John"

لاحظ أن الخاصية الجديدة fullName للقراءة فقط (معرّفة بواسطة كلمة val ) ولها دالة getter مخصصة ؛ إنه يلحق ببساطة الاسم الأول والأخير.

يجب تعيين جميع الخصائص في Kotlin عند الإعلان عنها أو وجودها في منشئ. هناك بعض الحالات التي لا يكون فيها ذلك مناسبًا ؛ على سبيل المثال ، للخصائص التي سيتم تهيئتها عبر حقن التبعية. في هذه الحالة ، يمكن استخدام معدِّل lateinit . هنا مثال:

 class MyClass { lateinit var firstName : String; fun inject() { firstName = "John"; } }

يمكن العثور على مزيد من التفاصيل حول العقارات في الوثائق الرسمية.

الميزة رقم 3: وراثة الصنف والبناة

يحتوي Kotlin على صياغة أكثر إيجازًا عندما يتعلق الأمر بالبناة أيضًا.

بناة

تحتوي فصول Kotlin على مُنشئ أساسي ومنشئ ثانوي واحد أو أكثر. مثال على تعريف المُنشئ الأساسي:

 class User constructor(firstName: String, lastName: String) { }

يذهب المُنشئ الأساسي بعد اسم الفئة في تعريف الفئة. إذا لم يكن للمنشئ الأساسي أي تعليقات توضيحية أو معدِّلات رؤية ، فيمكن حذف الكلمة الأساسية للمنشئ:

 class Person(firstName: String) { }

لاحظ أن المُنشئ الأساسي لا يمكن أن يكون له أي كود ؛ يجب إجراء أي تهيئة في كتلة init البرمجية الأولية:

 class Person(firstName: String) { init { //perform primary constructor initialization here } }

علاوة على ذلك ، يمكن استخدام المُنشئ الأساسي لتعريف الخصائص وتهيئتها:

 class User(var firstName: String, var lastName: String) { // ... }

تمامًا مثل الخصائص العادية ، يمكن أن تكون الخصائص المحددة من المُنشئ الأساسي ثابتة ( val ) أو قابلة للتغيير ( var ).

قد تحتوي الفصول الدراسية على منشآت ثانوية أيضًا ؛ بناء الجملة لتعريف واحد هو كما يلي:

 class User(var firstName: String, var lastName) { constructor(name: String, parent: Person) : this(name) { parent.children.add(this) } }

لاحظ أنه يجب على كل مُنشئ ثانوي التفويض إلى مُنشئ أساسي. هذا مشابه لـ Java ، التي تستخدم this الكلمة الأساسية:

 class User(val firstName: String, val lastName: String) { constructor(firstName: String) : this(firstName, "") { //... } }

عند إنشاء مثيل للفصول ، لاحظ أن Kotlin لا تحتوي على كلمات رئيسية new ، كما هو الحال مع Java. لإنشاء مثيل لفئة User المذكورة أعلاه ، استخدم:

 val user = User("John", "Doe)

إدخال الميراث

في Kotlin ، تمتد جميع الفئات من Any ، وهو مشابه Object في Java. بشكل افتراضي ، يتم إغلاق الفصول الدراسية ، مثل الفصول النهائية في Java. لذلك ، من أجل توسيع فئة ، يجب إعلانها على أنها open أو abstract :

 open class User(val firstName, val lastName) class Administrator(val firstName, val lastName) : User(firstName, lastName)

لاحظ أنه يتعين عليك تفويض المُنشئ الافتراضي للفئة الموسعة ، وهو ما يشبه استدعاء طريقة super() في مُنشئ فئة جديدة في Java.

لمزيد من التفاصيل حول الفصول ، تحقق من الوثائق الرسمية.

الميزة رقم 4: تعبيرات لامدا

تعد تعبيرات Lambda ، التي يتم تقديمها باستخدام Java 8 ، إحدى ميزاتها المفضلة. ومع ذلك ، فإن الأشياء ليست مشرقة جدًا على Android ، حيث لا تزال تدعم Java 7 فقط ، ويبدو أن Java 8 لن يتم دعمها في أي وقت قريبًا. لذا ، فإن الحلول البديلة ، مثل Retrolambda ، تجلب تعبيرات lambda إلى Android.

مع Kotlin ، ليست هناك حاجة إلى مكتبات أو حلول بديلة إضافية.

الوظائف في Kotlin

لنبدأ بالانتقال سريعًا إلى بناء جملة الدالة في Kotlin:

 fun add(x: Int, y: Int) : Int { return x + y }

يمكن حذف القيمة المرجعة للدالة ، وفي هذه الحالة ، ستُرجع الدالة Int . يجدر بنا أن نكرر أن كل شيء في Kotlin هو كائن ممتد من Any ، ولا توجد أنواع بدائية.

يمكن أن تحتوي وسيطة الدالة على قيمة افتراضية ، على سبيل المثال:

 fun add(x: Int, y: Int = 1) : Int { return x + y; }

في هذه الحالة ، يمكن استدعاء الوظيفة add() بتمرير الوسيطة x فقط. كود Java المكافئ سيكون:

 int add(int x) { Return add(x, 1); } int add(int x, int y) { return x + y; }

شيء جميل آخر عند استدعاء دالة هو أنه يمكن استخدام الوسائط المسماة. علي سبيل المثال:

 add(y = 12, x = 5)

لمزيد من التفاصيل حول الوظائف ، تحقق من الوثائق الرسمية.

استخدام تعبيرات لامدا في كوتلن

يمكن النظر إلى تعبيرات Lambda في Kotlin على أنها وظائف مجهولة في Java ، ولكن مع بناء جملة أكثر إيجازًا. كمثال ، دعنا نوضح كيفية تنفيذ مستمع النقرات في Java و Kotlin.

في جافا:

 view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(v.getContext(), "Clicked on view", Toast.LENGTH_SHORT).show(); } };

في Kotlin:

 view.setOnClickListener({ view -> toast("Click") })

رائع! سطر واحد فقط من التعليمات البرمجية! يمكننا أن نرى أن تعبير لامدا محاط بأقواس معقوفة. يتم الإعلان عن المعلمات أولاً ، ويتبع الجسم علامة -> . باستخدام مستمع النقر ، لم يتم تحديد نوع معلمة العرض حيث يمكن استنتاجها. الجسد هو مجرد استدعاء لوظيفة toast() لإظهار الخبز المحمص ، والتي توفرها Kotlin.

أيضًا ، إذا لم يتم استخدام المعلمات ، فيمكننا استبعادها:

 view.setOnClickListener({ toast("Click") })

قام Kotlin بتحسين مكتبات Java ، ويمكن استدعاء أي وظيفة تتلقى واجهة مع طريقة واحدة للوسيطة باستخدام وسيطة دالة (بدلاً من Interface).

علاوة على ذلك ، إذا كانت الوظيفة هي المعلمة الأخيرة ، فيمكن نقلها خارج الأقواس:

 view.setOnClickListener() { toast("Click") }

أخيرًا ، إذا كانت الوظيفة تحتوي على معلمة واحدة فقط وهي دالة ، فيمكن ترك الأقواس:

 view.setOnClickListener { toast("Click") }

لمزيد من المعلومات ، راجع كتاب Kotlin لمطوري Android من تأليف أنطونيو ليفا والوثائق الرسمية.

وظائف التمديد

يوفر Kotlin ، على غرار C # ، القدرة على توسيع الفئات الحالية بوظائف جديدة باستخدام وظائف الامتداد. على سبيل المثال ، طريقة الامتداد التي من شأنها حساب تجزئة String لسلسلة:

 fun String.md5(): ByteArray { val digester = MessageDigest.getInstance("MD5") digester.update(this.toByteArray(Charset.defaultCharset())) return digester.digest() }

لاحظ أن اسم الوظيفة يسبقه اسم الفئة الموسعة (في هذه الحالة ، String ) ، وأن مثيل الفئة الموسعة متاح عبر this الكلمة الأساسية.

وظائف الامتداد تعادل وظائف الأداة المساعدة Java. ستبدو دالة المثال في Java بالشكل التالي:

 public static int toNumber(String instance) { return Integer.valueOf(instance); }

يجب وضع دالة المثال في فئة الأداة المساعدة. ما يعنيه ذلك هو أن وظائف الامتداد لا تعدل الفئة الموسعة الأصلية ، ولكنها طريقة ملائمة لكتابة طرق الأداة المساعدة.

الميزة رقم 5: Null-Safety

يعد NullPointerException أحد أكثر الأشياء التي تصطدم بها في Java. Null-safety هي ميزة تم دمجها في لغة Kotlin وهي ضمنية للغاية ولا داعي للقلق بشأنها. تنص الوثائق الرسمية على أن الأسباب المحتملة الوحيدة NullPointerExceptions هي:

  • دعوة صريحة لرمي NullPointerException .
  • باستخدام !! عامل التشغيل (الذي سأشرح لاحقًا).
  • كود جافا الخارجي.
  • إذا تم الوصول إلى الخاصية lateinit في المنشئ قبل تهيئتها ، فسيتم طرح UninitializedPropertyAccessException .

بشكل افتراضي ، تعتبر جميع المتغيرات والخصائص في Kotlin non-null (غير قادرة على الاحتفاظ بقيمة null ) إذا لم يتم الإعلان عنها صراحةً أنها لاغية. كما ذكرنا سابقًا ، لتعريف متغير لقبول قيمة null ، يجب إضافة علامة استفهام بعد النوع. علي سبيل المثال:

 val number: Int? = null

ومع ذلك ، لاحظ أن الكود التالي لن يتم تجميعه:

 val number: Int? = null number.toString()

هذا لأن المترجم يقوم بإجراء فحوصات null . للترجمة ، يجب إضافة تحقق null :

 val number: Int? = null if(number != null) { number.toString(); }

سيتم ترجمة هذا الرمز بنجاح. ما يفعله Kotlin في الخلفية ، في هذه الحالة ، هل يصبح هذا number nun-null ( Int بدلاً من Int? ) داخل كتلة if.

يمكن تبسيط عملية التحقق من null باستخدام عامل التشغيل الآمن ( ?. ):

 val number: Int? = null number?.toString()

سيتم تنفيذ السطر الثاني فقط إذا لم يكن الرقم null . يمكنك حتى استخدام عامل التشغيل Elvis الشهير ( ?: :):

 val number Int? = null val stringNumber = number?.toString() ?: "Number is null"

إذا لم يكن التعبير الموجود على يسار ?: null ، فيتم تقييمه وإعادته. خلاف ذلك ، يتم إرجاع نتيجة التعبير الموجود على اليمين. شيء رائع آخر هو أنه يمكنك استخدام throw أو return على الجانب الأيمن من عامل Elvis لأنها تعبيرات في Kotlin. علي سبيل المثال:

 fun sendMailToUser(user: User) { val email = user?.email ?: throw new IllegalArgumentException("User email is null") //... }

ال !! المشغل أو العامل

إذا كنت ترغب في طرح NullPointerException بنفس الطريقة كما في Java ، يمكنك فعل ذلك باستخدام !! المشغل أو العامل. الكود التالي سوف يرمي إلى NullPointerException :

 val number: Int? = null number!!.toString()

يصب

تم الإرسال باستخدام كلمة as :

 val x: String = y as String

يعتبر هذا اختيارًا "غير آمن" ، حيث سيؤدي إلى طرح ClassCastException إذا لم يكن التمثيل ممكنًا ، كما يفعل Java. هناك عامل تشغيل "آمن" يُرجع القيمة null بدلاً من طرح استثناء:

 val x: String = y as? String

لمزيد من التفاصيل حول الصب ، تحقق من قسم Type Casts and Casts في الوثائق الرسمية ، ولمزيد من التفاصيل حول السلامة null ، تحقق من قسم Null-Safety.

خصائص lateinit

هناك حالة يمكن أن يتسبب فيها استخدام خصائص lateinit في استثناء مشابه لـ NullPointerException . ضع في اعتبارك الفئة التالية:

 class InitTest { lateinit var s: String; init { val len = this.s.length } }

سيتم ترجمة هذا الرمز دون سابق إنذار. ومع ذلك ، بمجرد إنشاء مثيل TestClass ، سيتم طرح UninitializedPropertyAccessException لأنه يتم الوصول إلى الخاصية s قبل تهيئتها.

الميزة رقم 6: وظيفة with()

الوظيفة with() مفيدة وتأتي مع مكتبة Kotlin القياسية. يمكن استخدامه لحفظ بعض الكتابة إذا كنت بحاجة إلى الوصول إلى العديد من خصائص الكائن. علي سبيل المثال:

 with(helloWorldTextView) { text = "Hello World!" visibility = View.VISIBLE }

يتلقى كائنًا ووظيفة امتداد كمعلمات. كتلة الكود (في الأقواس المتعرجة) هي تعبير لامدا لوظيفة التمديد للكائن المحدد كمعامل أول.

الميزة رقم 7: زيادة الحمولة على المشغل

باستخدام Kotlin ، يمكن توفير تطبيقات مخصصة لمجموعة محددة مسبقًا من المشغلين. لتنفيذ عامل ، يجب توفير وظيفة عضو أو وظيفة ملحق بالاسم المحدد.

على سبيل المثال ، لتنفيذ عامل الضرب ، يجب توفير دالة عضو أو دالة امتداد ، مع times(argument) :

 operator fun String.times(b: Int): String { val buffer = StringBuffer() for (i in 1..b) { buffer.append(this) } return buffer.toString() }

يوضح المثال أعلاه تنفيذ عامل ثنائي * على String . على سبيل المثال ، سيقوم التعبير التالي بتعيين القيمة " newString " لمتغير سلسلة جديد:

 val newString = "Test" * 4

نظرًا لأنه يمكن استخدام وظائف الامتداد ، فهذا يعني أنه يمكن تغيير السلوك الافتراضي للمشغلين لجميع الكائنات. هذا سيف ذو حدين ويجب الحذر منه. للحصول على قائمة بأسماء الوظائف لجميع المشغلين الذين يمكن تحميلهم فوق طاقتهم ، تحقق من الوثائق الرسمية.

هناك اختلاف كبير آخر مقارنة بجافا ، وهما == و != عاملي التشغيل. عامل التشغيل == يترجم إلى:

 a?.equals(b) ?: b === null

بينما عامل التشغيل != يترجم إلى:

 !(a?.equals(b) ?:

ما يعنيه ذلك ، هو أن استخدام == لا يؤدي إلى التحقق من الهوية كما هو الحال في Java (قارن ما إذا كانت مثيلات الكائن هي نفسها) ، ولكنه يتصرف بنفس طريقة أسلوب equals() جنبًا إلى جنب مع عمليات التحقق من القيمة null .

لإجراء فحص الهوية ، يجب استخدام عوامل التشغيل === و !== في Kotlin.

الميزة رقم 8: الخصائص المفوضة

تشترك بعض الخصائص في بعض السلوكيات الشائعة. على سبيل المثال:

  • الخصائص المهيأة الكسولة التي تمت تهيئتها عند الوصول الأول.
  • الخصائص التي تقوم بتطبيق Observable في نمط Observer.
  • الخصائص المخزنة في الخريطة بدلاً من ذلك كحقول منفصلة.

لتسهيل تنفيذ مثل هذه الحالات ، تدعم Kotlin الخصائص المفوضة :

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

هذا يعني أن دالات getter و setter للخاصية p تتم معالجتها بواسطة مثيل لفئة أخرى ، Delegate .

مثال على مفوض لخاصية String :

 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.'") } }

المثال أعلاه يطبع رسالة عند تعيين خاصية أو قراءتها.

يمكن إنشاء المندوبين لكل من الخصائص القابلة للتغيير ( var ) والقراءة فقط ( val ).

بالنسبة لخاصية القراءة فقط ، يجب تنفيذ طريقة getValue . يأخذ معاملين (مأخوذين من الوثائق الرسمية):

  • المتلقي - يجب أن يكون هو نفسه أو نوعًا فائقًا لمالك الملكية (لخصائص الامتداد ، هذا هو النوع الذي يتم تمديده).
  • البيانات الوصفية - يجب أن تكون من النوع KProperty<*> أو من النوع الفائق.

يجب أن تُرجع هذه الدالة نفس نوع الخاصية أو نوعها الفرعي.

بالنسبة للخاصية القابلة للتغيير ، يجب على المفوض أن يوفر بالإضافة إلى ذلك وظيفة باسم setValue تأخذ المعلمات التالية:

  • المتلقي - نفس الشيء بالنسبة لـ getValue() .
  • البيانات الوصفية - مثل getValue() .
  • new value - يجب أن تكون من نفس نوع الخاصية أو نوعها الفائق.

هناك عدد قليل من المندوبين المعياريين الذين يأتون مع Kotlin والذين يغطون المواقف الأكثر شيوعًا:

  • كسول
  • يمكن ملاحظتها
  • قابل للنقض

كسول

Lazy هو مفوض قياسي يأخذ تعبير lambda كمعامل. يتم تنفيذ تعبير lambda الذي تم تمريره في المرة الأولى التي يتم فيها استدعاء طريقة getValue() .

افتراضيًا ، تتم مزامنة تقييم الخصائص البطيئة. إذا لم تكن مهتمًا بالترابط المتعدد ، فيمكنك استخدام lazy(LazyThreadSafetyMode.NONE) { … } للحصول على أداء إضافي.

يمكن ملاحظتها

Delegates.observable() مخصص للخصائص التي يجب أن تتصرف كملاحظة في نمط المراقب. يقبل معلمتين ، القيمة الأولية والدالة التي تحتوي على ثلاث وسيطات (الخاصية والقيمة القديمة والقيمة الجديدة).

سيتم تنفيذ تعبير lambda المحدد في كل مرة يتم فيها استدعاء طريقة setValue() :

 class User { var email: String by Delegates.observable("") { prop, old, new -> //handle the change from old to new value } }

قابل للنقض

هذا المفوض القياسي هو نوع خاص من الملاحظة التي تتيح لك تحديد ما إذا كانت القيمة الجديدة المعينة لخاصية ما سيتم تخزينها أم لا. يمكن استخدامه للتحقق من بعض الشروط قبل تعيين قيمة. كما هو الحال مع Delegates.observable() ، فإنه يقبل معلمتين: القيمة الأولية ، والدالة.

الفرق هو أن الدالة ترجع قيمة منطقية. إذا تم إرجاعه true ، فسيتم تخزين القيمة الجديدة المخصصة للخاصية ، أو يتم تجاهلها بأي طريقة أخرى.

 var positiveNumber = Delegates.vetoable(0) { d, old, new -> new >= 0 }

سيخزن المثال المحدد فقط الأرقام الموجبة التي تم تعيينها للممتلكات.

لمزيد من التفاصيل ، تحقق من الوثائق الرسمية.

الميزة رقم 9: تعيين كائن إلى الخريطة

حالة الاستخدام الشائعة هي تخزين قيم الخصائص داخل الخريطة. يحدث هذا غالبًا في التطبيقات التي تعمل مع واجهات برمجة تطبيقات RESTful وتوزع كائنات JSON. في هذه الحالة ، يمكن استخدام مثيل الخريطة كمفوض لخاصية مفوضة. مثال من الوثائق الرسمية:

 class User(val map: Map<String, Any?>) { val name: String by map val age: Int by map }

في هذا المثال ، يمتلك User مُنشئًا أساسيًا يأخذ خريطة. ستأخذ الخاصيتان القيمتين من الخريطة التي تم تعيينها ضمن المفاتيح التي تساوي أسماء الخصائص:

 val user = User(mapOf( "name" to "John Doe", "age" to 25 ))

سيتم تعيين خاصية الاسم لمثيل المستخدم الجديد بقيمة "John Doe" وخاصية العمر بالقيمة 25.

يعمل هذا مع خصائص var بالاشتراك مع MutableMap أيضًا:

 class MutableUser(val map: MutableMap<String, Any?>) { var name: String by map var age: Int by map }

الميزة رقم 10: المجموعات والعمليات الوظيفية

بدعم من lambdas في Kotlin ، يمكن رفع المجموعات إلى مستوى جديد.

بادئ ذي بدء ، يميز Kotlin بين المجموعات المتغيرة وغير القابلة للتغيير. على سبيل المثال ، هناك إصداران من الواجهة القابلة للتكرار :

  • متوقعة
  • متغير

وينطبق الشيء نفسه على واجهات التجميع والقائمة والتعيين والخريطة .

على سبيل المثال ، هذه any عملية تعود true إذا كان هناك عنصر واحد على الأقل يطابق المسند المحدد:

 val list = listOf(1, 2, 3, 4, 5, 6) assertTrue(list.any { it % 2 == 0 })

للحصول على قائمة شاملة بالعمليات الوظيفية التي يمكن إجراؤها على المجموعات ، تحقق من منشور المدونة هذا.

خاتمة

لقد خدشنا للتو سطح ما تقدمه Kotlin. للمهتمين بمزيد من القراءة ومعرفة المزيد ، تحقق من:

  • منشورات مدونة Kotlin الخاصة بـ أنطونيو ليفا وكتابها.
  • الوثائق الرسمية والبرامج التعليمية من JetBrains.

باختصار ، يوفر لك Kotlin القدرة على توفير الوقت عند كتابة تطبيقات Android الأصلية باستخدام صيغة بديهية وموجزة. لا تزال لغة برمجة حديثة ، ولكن في رأيي ، أصبحت الآن مستقرة بدرجة كافية لاستخدامها في إنشاء تطبيقات الإنتاج.

فوائد استخدام لغة Kotlin:

  • دعم Android Studio سلس وممتاز.
  • من السهل تحويل مشروع Java موجود إلى Kotlin.
  • قد يتواجد كود Java و Kotlin في نفس المشروع.
  • لا توجد سرعة تحميل في التطبيق.

السلبيات:

  • ستضيف Kotlin مكتباتها إلى ملف .apk . الذي تم إنشاؤه ، وبالتالي فإن حجم .apk النهائي سيكون أكبر بحوالي 300 كيلوبايت.
  • في حالة إساءة الاستخدام ، يمكن أن يؤدي التحميل الزائد على المشغل إلى رمز غير قابل للقراءة.
  • يتصرف IDE والإكمال التلقائي بشكل أبطأ قليلاً عند العمل مع Kotlin مقارنة بمشاريع Java Android الخالصة.
  • يمكن أن تكون أوقات التجميع أطول قليلاً.