تعلَّم لغة البرمجة Kotlin.

Kotlin هي لغة برمجة شائعة الاستخدام من قبل مطوّري برامج Android في كل مكان. يعمل هذا الموضوع كلغة Kotlin هذه الدورة لتتعرف على كيفية بدء العمل بسرعة.

تعريف المتغيّر

تستخدم لغة Kotlin كلمتَين رئيسيتَين مختلفتَين للإعلان عن المتغيّرات: val وvar.

  • استخدِم val لمتغيّر لا تتغيّر قيمته أبدًا. لا يمكنك إعادة تعيين قيمة إلى متغيّر تم تعريفه باستخدام val.
  • استخدِم var لمتغيّر يمكن تغيير قيمته.

في المثال أدناه، count هو متغيّر من النوع Int تم تخصيصه القيمة الأولية 10:

var count: Int = 10

Int هو نوع يمثل عددًا صحيحًا، وهو أحد الأنواع العديدة التي تمثل يمكن تمثيله في Kotlin. على غرار اللغات الأخرى، يمكنك أيضًا استخدام Byte وShort وLong وFloat وDouble استنادًا إلى بياناتك الرقمية

تعني الكلمة الرئيسية var أنه يمكنك إعادة تعيين القيم إلى count حسب الحاجة. بالنسبة على سبيل المثال، يمكنك تغيير قيمة count من 10 إلى 15:

var count: Int = 10
count = 15

على الرغم من ذلك، ليس من المفترض تغيير بعض القيم. فكّر في String يسمى languageName إذا أردت التأكد من أنّ languageName يشتمل دائمًا على قيمة من "Kotlin"، فيمكنك حينئذٍ الإعلان عن languageName باستخدام الكلمة الرئيسية val:

val languageName: String = "Kotlin"

تتيح لك هذه الكلمات الرئيسية أن تكون واضحًا بشأن ما يمكن تغييره. يمكنك استخدامها من أجل لصالحك حسب الحاجة. إذا كان يجب أن يكون مرجع متغير قابلاً لإعادة التعيين، الإعلان عنه على أنّه var. وبخلاف ذلك، يمكنك استخدام val.

كتابة الاستنتاج

متابعة للمثال السابق، عندما تقوم بتعيين قيمة أولية إلى languageName، يمكن للمحول البرمجي لـ Kotlin استنتاج النوع بناءً على نوع والقيمة المعينة.

نظرًا لأن قيمة "Kotlin" من النوع String، يستنتج المُحول البرمجي أن languageName أيضًا String. لاحظ أن لغة Kotlin تتم كتابتها بشكل ثابت . وهذا يعني أن النوع يتم حله في وقت التجميع ولا يتم حله أبدًا التغييرات.

في المثال التالي، يتم استنتاج languageName على أنّه String، لذا لا يمكنك لطلب أي دوال ليست جزءًا من الفئة String:

val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()

// Fails to compile
languageName.inc()

toUpperCase() هي دالة لا يمكن استدعاؤها إلا على متغيرات من النوع String ولأن المحول البرمجي لـ Kotlin قد استنتج أن languageName على أنه String، يمكنك الاتصال بـ toUpperCase() بأمان. في المقابل، inc() هو عامل تشغيل Int. بحيث لا يمكن استدعاؤه في String. أسلوب Kotlin في الكتابة والاستنتاج يمنحك الإيجاز وسلامة الكتابة.

السلامة الخالية

في بعض اللغات، يمكن الإعلان عن متغير من نوع المرجع بدون توفير قيمة أولية صريحة. في هذه الحالات، تحتوي المتغيرات عادةً على قيمة لا يمكن أن تحتوي متغيّرات Kotlin على قيم فارغة تلقائيًا. وهذا يعني أن المقتطف التالي غير صالح:

// Fails to compile
val languageName: String = null

لكي يحتوي متغير على قيمة فارغة، يجب أن يكون من النوع nullable. يمكنك تحديد متغيّر على أنّه قابل للقيم الفارغة من خلال إضافة لاحقة إلى نوعه بـ ?، على النحو الموضّح في المثال التالي:

val languageName: String? = null

باستخدام النوع String?، يمكنك تحديد قيمة String أو null إلى languageName

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

توفر لغة Kotlin عددًا من آليات العمل بأمان باستخدام القيم الفارغة المتغيرات. لمزيد من المعلومات، يُرجى مراجعة أنماط لغة Kotlin الشائعة في Android: قابلية عدم القيم

الشرطية

تتميز لغة Kotlin بآليات متعددة لتطبيق المنطق الشرطي. الأكثر شائعة هذه هي عبارة if-else. إذا كان تعبير محاطًا في بين قوسين بجانب أي كلمة رئيسية if يتم تقييمها إلى true، ثم يتم تقييم الرمز ضمن هذا الفرع (أي التعليمة البرمجية التي تلي مباشرة والمُلتفة بشكل مجعد) الأقواس). وبخلاف ذلك، يتم تنفيذ الرمز داخل فرع else.

if (count == 42) {
    println("I have the answer.")
} else {
    println("The answer eludes me.")
}

يمكنك تمثيل شروط متعدّدة باستخدام else if. يتيح لك هذا تمثيل منطقًا أكثر دقة وتعقيدًا داخل عبارة شرطية واحدة، كما هو موضح في في المثال التالي:

if (count == 42) {
    println("I have the answer.")
} else if (count > 35) {
    println("The answer is close.")
} else {
    println("The answer eludes me.")
}

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

val answerString: String = if (count == 42) {
    "I have the answer."
} else if (count > 35) {
    "The answer is close."
} else {
    "The answer eludes me."
}

println(answerString)

وبشكل ضمني، يعرض كل فرع شرطي نتيجة التعبير على السطر الأخير، لذا لن تحتاج إلى استخدام كلمة return الرئيسية. نظرًا لأن نتيجة جميع الفروع الثلاثة من النوع String، يكون ناتج التعبير if-else هو أيضًا من النوع String. في هذا المثال، يتم تعيين الحرف الأول من نوع answerString من نتيجة تعبير if-else. يمكن استخدام الاستنتاج النوعي من النوع الصريح لـ answerString، إلا أنه غالبًا ما يكون اسم فكرة تضمينها من أجل الوضوح.

مع تزايد تعقيد عبارتك الشرطية، قد تفكر استبدال تعبير if-else بتعبير when، كما هو موضح في المثال التالي:

val answerString = when {
    count == 42 -> "I have the answer."
    count > 35 -> "The answer is close."
    else -> "The answer eludes me."
}

println(answerString)

يتم تمثيل كل فرع في تعبير when بشرط، وهو سهم (->)، ونتيجة لذلك. إذا كان الشرط الموجود على الجانب الأيسر من السهم إلى true، فإن نتيجة التعبير في الجانب الأيمن هي عاد. لاحظ أن التنفيذ لا يمر من فرع إلى آخر. الرمز في مثال التعبير when يماثل من الناحية الوظيفية مع ذلك في المثال السابق ولكن يمكن القول إنها أسهل في القراءة.

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

val languageName: String? = null
if (languageName != null) {
    // No need to write languageName?.toUpperCase()
    println(languageName.toUpperCase())
}

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

الدوال

يمكنك تجميع تعبير واحد أو أكثر في دالة. بدلاً من تكرار نفس سلسلة التعبيرات في كل مرة تحتاج فيها إلى نتيجة، فيمكنك التفاف التعبيرات في دالة واستدعاء هذه الدالة بدلاً من ذلك.

للإعلان عن دالة، استخدِم الكلمة الرئيسية fun متبوعة باسم الدالة. بعد ذلك، عليك تحديد أنواع المدخلات التي تستخدمها الدالة، إن توفّرت، وتعريفها ونوع الإخراج الذي ينتجه. نص الدالة هو المكان الذي تحدد فيه التعبيرات التي يتم استدعاؤها عند استدعاء الدالة.

بناءً على الأمثلة السابقة، إليك دالة Kotlin الكاملة:

fun generateAnswerString(): String {
    val answerString = if (count == 42) {
        "I have the answer."
    } else {
        "The answer eludes me"
    }

    return answerString
}

تحمل الدالة في المثال أعلاه اسم generateAnswerString. أُنشأها جون هنتر، الذي كان متخصصًا لا يأخذ أي إدخال. وهي تنتج نتيجة من النوع String. لإجراء مكالمة ، استخدم اسمها، متبوعًا بعامل الاستدعاء (()). في جلسة المعمل، في المثال أدناه، يتم إعداد المتغير answerString بالنتيجة من generateAnswerString()

val answerString = generateAnswerString()

يمكن أن تأخذ الدوال الوسيطات كإدخال، كما هو موضح في المثال التالي:

fun generateAnswerString(countThreshold: Int): String {
    val answerString = if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }

    return answerString
}

عند الإعلان عن دالة، يمكنك تحديد أي عدد من الوسيطات الأنواع. في المثال أعلاه، تأخذ generateAnswerString() وسيطة واحدة باسم countThreshold من النوع Int. داخل الدالة، يمكنك الرجوع إلى الوسيطة باستخدام اسمها.

عند استدعاء هذه الدالة، يجب تضمين وسيطة داخل الدالة أقواس الاتصال:

val answerString = generateAnswerString(42)

تبسيط تعريفات الدوال

generateAnswerString() هي دالة بسيطة إلى حد ما. تعلن الدالة عن المتغير ثم يتم إرجاعه فورًا. عندما تكون نتيجة تعبير واحد عن دالة، يمكنك تخطي إعلان متغير محلي عن طريق عرض نتيجة التعبير if-else المضمّن في الدالة، مثل كما هو موضح في المثال التالي:

fun generateAnswerString(countThreshold: Int): String {
    return if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }
}

يمكنك أيضًا استبدال الكلمة الرئيسية للعرض بعامل تشغيل التعيين:

fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
        "I have the answer"
    } else {
        "The answer eludes me"
    }

الدوال المجهولة

لا تحتاج كل دالة إلى اسم. يتم تحديد بعض الدوال بشكل مباشر أكثر عن طريق إدخالاتهم ومخرجاتهم. وتُسمّى هذه الدوال الدوال المجهولة. إِنْتَ يمكننا الاحتفاظ بمرجع لدالة مجهولة، باستخدام هذا المرجع لاستدعاء الدالة المجهولة لاحقًا. يمكنك أيضًا تمرير المرجع حول التطبيق، كما هو الحال مع أنواع المراجع الأخرى.

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

مثل الدوال المُسمّاة، يمكن أن تحتوي الدوال المجهولة على أي عدد من التعبيرات. تكون القيمة المعروضة للدالة هي نتيجة التعبير النهائي.

في المثال أعلاه، يحتوي stringLengthFunc على إشارة إلى مستخدم مجهول. الدالة التي تستخدم String كإدخال وتعرض طول المدخل String كناتج من النوع Int. لهذا السبب، فإن نوع الدالة يُشار إليه بالاختصار (String) -> Int. ومع ذلك، لا تستدعي هذه التعليمة البرمجية الدالة. لاسترداد نتيجة الدالة، يجب عليك استدعاؤها مثلما تفعل الدالة المُسماة. يجب توفير String عند الاتصال بـ stringLengthFunc، نظرًا كما هو موضح في المثال التالي:

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

val stringLength: Int = stringLengthFunc("Android")

الدوال ذات الترتيب الأعلى

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

في ما يلي مثال على دالة ذات ترتيب أعلى:

fun stringMapper(str: String, mapper: (String) -> Int): Int {
    // Invoke function
    return mapper(str)
}

تستخدم الدالة stringMapper() الرمز String مع دالة يستمد القيمة Int من String الذي تضعه فيه.

يمكنك استدعاء stringMapper() من خلال تمرير String ودالة تستوفي معلمة الإدخال الأخرى، وهي الدالة التي تستخدم String إدخال وإخراج Int، كما هو موضح في المثال التالي:

stringMapper("Android", { input ->
    input.length
})

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

stringMapper("Android") { input ->
    input.length
}

يمكن العثور على الدوال المجهولة في مكتبة Kotlin القياسية. بالنسبة مزيد من المعلومات، راجع الدوال ذات الترتيب الأعلى وملفات lambda.

صفوف

وجميع الأنواع المذكورة حتى الآن مضمّنة في لغة البرمجة Kotlin. . إذا كنت ترغب في إضافة نوعك المخصص، يمكنك تحديد فئة باستخدام الكلمة الرئيسية class، كما هو موضّح في المثال التالي:

class Car

الخصائص

وتمثل الفئات الولاية باستخدام الخصائص. حاسمة الموقع هو متغير على مستوى الفئة يمكن أن يتضمن getter، وsetter، وحقلًا خلفيًا. بما أنّ السيارة تحتاج إلى عجلات لقيادة السيارة، يمكنك إضافة قائمة تتضمّن عناصر Wheel باعتبارها خاصية Car، كما هو موضح في المثال التالي:

class Car {
    val wheels = listOf<Wheel>()
}

يُرجى العِلم أنّ wheels هو public val، ما يعني أنّه يمكن الوصول إلى wheels من خلال خارج الفئة Car، ولا يمكن إعادة تعيينه. إذا كنت ترغب في الحصول على مثيل لـ Car، يجب عليك أولاً استدعاء الدالة الإنشائية الخاصة به. من هناك، يمكنك الوصول إلى أي من خصائصه التي يمكن الوصول إليها.

val car = Car() // construct a Car
val wheels = car.wheels // retrieve the wheels value from the Car

إذا كنت تريد تخصيص العجلات، فيمكنك تحديد دالة إنشائية مخصصة كيفية إعداد خصائص الفئة:

class Car(val wheels: List<Wheel>)

في المثال أعلاه، تعتبر الدالة الإنشائية للفئة List<Wheel> على أنها الوسيطة الإنشائية وتستخدم هذه الوسيطة لتهيئة wheels الموقع.

دوال الفئات والتغليف

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

في المثال التالي، يتم الحفاظ على خصوصية السمة doorLock من أي شيء. خارج الصف Car. لفتح قفل السيارة، يجب الاتصال بالرقم unlockDoor(). تمرير الدالة بمفتاح صالح، كما هو موضح في المثال التالي:

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

إذا كنت تريد تخصيص كيفية الإشارة إلى موقع، يمكنك تقديم لكل من getter and setter. على سبيل المثال، إذا كنت تريد عرض الصفحة مع تقييد الوصول إلى أداة الضبط، يمكنك تعيين هذه الدالة على أنها private:

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    var gallonsOfFuelInTank: Int = 15
        private set

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

باستخدام مجموعة من الخصائص والدوال، يمكنك إنشاء فئات لإنشاء نموذج لجميع أنواع الكائنات.

إمكانية التشغيل التفاعلي

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

الخطوات التالية

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