البنية العامّة للغة Kotlin

من موسوعة حسوب
مراجعة 06:41، 21 فبراير 2018 بواسطة عبد اللطيف ايمش (نقاش | مساهمات) (تنسيق الشيفرات وتصحيح رابط)

تعريف الحزمة (Package)

يُكتب توصيف الحزمة (package) في بداية الملف المصدريّ (source file) بالشكل الآتي:

package my.demo
import java.util.*
// ...

ولا يُشترط التوافق ما بين الحزمة (package) والمجلد الموجودة فيه (directory)، إذ من الممكن أن تتوضع الملفات المصدريّة عشوائيًّا في نظام الملفات.

المزيد عن الحزم packages.

تعريف الدالة (Function)

إن كانت الدالة بمتحولين من نوع Int وتعيد قيمةً بنوع Int أيضًا، يصبح تعريفها بالشكل:

fun sum(a: Int, b: Int): Int {
   return a + b
}

أما الشيفرة الآتية فهي لتعريف دالةٍ باسم sum تحتوي على تعبير (expression) دون التصريح عن نوع القيمة المعادة:

fun sum(a: Int, b: Int) = a + b

وإن لم تُعد الدالة أية قيمةٍ بمعنىً يصبح تعريفها على النحو الآتي: (الحالة الافتراضية هي Unit)

fun printSum(a: Int, b: Int): Unit {
   println("sum of $a and $b is ${a + b}")
}

ويمكن الاستغناء عن النوع المُعاد Unit ليصبح التعريف بالشكل:

fun printSum(a: Int, b: Int) {
   println("sum of $a and $b is ${a + b}")
}

المزيد عن الدوال functions.

تعريف المتحولات (Variables)

لتعريف متحوِّلٍ محليٍّ بإسنادٍ وحيد (للقراءة فقط ولا يمكن إعطاؤه قيمةً جديدة) تُستخدَم إحدى الصيغ الآتية:

val a: Int = 1  // إسناد مباشر

val b = 2   // يُعرَّف ضمنيًّا من النوع Int 

val c: Int  // من الضروري تحديد النوع إن لم تُسند القيمة للمتحوّل مباشرةً

c = 3       // إسنادٌ لمتحوّل مُعرَّف مسبقًا

ولتعريف متحوِّلٍ بقيمةٍ قابلةٍ للتعديل:

var x = 5 // يُعرَّف ضمنيًا من النوع Int 

x += 1

وقد تكون المتحولات واقعةً بمستوىً أعلى من البنية الحاليّة كما في المثال الآتي: (المتحولان PI و x يقعان في مستوى أعلى من الدالة incrementX)

val PI = 3.14
var x = 0

fun incrementX() {
   x += 1
}

المزيد عن الخاصّيات properties والحقول fields.

التعليقات (Comments)

تدعم لغة Kotlin نوعين من التعليقات (كما هو الحال في لغتَي Java و JavaScript)؛ وهما التعليقات السطريّة أو التعليقات بأكثر من سطر، وتكون صياغتهما بالشكل الآتي:

//‎‎ تعليق بسطر واحد

/*‎‎ هذا التعليق
بأكثر من سطر */

وعلى عكس لغة Java يُمكن للتعليقات بأكثر من سطرٍ أن تتداخل (أي أن يقع تعليقٌ داخلَ تعليقٍ آخر).

ولمعرفة المزيد من التفاصيل عن التعليقات يمكن الانتقال لصفحة توضيح آلية التوثيق للبرامج بلغة Kotlin بالانكليزية.

استخدام قوالب السلاسل النصيّة (String Templates)

تكون صياغتها بالشكل الآتي:

var a = 1
//‎‎ اسم بسيط ضمن قالب نصيّ
val s1 = "a is $a"

a = 2
//‎‎ تعبيرٌ عشوائيّ ضمن قالب نصيّ
val s2 = "${s1.replace("is", "was")}, but now is $a"

المزيد عن قوالب السلاسل النصية.

استخدام التعابير الشرطية (Conditional Expressions)

توضِّح الدالة الآتية (لإعادة القيمة الأكبر من بين القيمتين a و b) طريقة صياغة الشرط if:

fun maxOf(a: Int, b: Int): Int {
   if (a > b) {
       return a
   } else {
       return b
   }
}

وكما يمكن استخدام الشرط if بصياغة تعبيريّة بالشكل الآتي:

fun maxOf(a: Int, b: Int) = if (a > b) a else b

المزيد عن استخدام الشرط if.

استخدام القيم التي تقبل القيمة الفارغة Null والتحقُّق منها

عندما تكون القيمة null محتملة للمتحول المرجعي (reference) فيُقال عنه أنه nullable.

إن الدالة الآتية ستعيد قيمة null إن لم تحتوي السلسلة str على قيمةٍ صحيحة:

fun parseInt(str: String): Int? {
   // ...
}

أما الدالة الآتية (إخراج قيمة ناتج جداء العددين الموجودين بالسلسلتين arg1 و arg2) فتعيد قيمة null:

fun printProduct(arg1: String, arg2: String) {
   val x = parseInt(arg1) //‎‎ تحويل السلسلة النصيّة إلى قيمة عددية تُخزّن في المتحول
   val y = parseInt(arg2)

   //‎‎ قد ينتج عن عملية الجداء حدوث خطأ في حال احتواء أحد المتحولين على قيمة null
   if (x != null && y != null) {
       println(x * y)
   }

   else {
       println("either '$arg1' or '$arg2' is not a number")
   }    
}

أو بالشكل:

// ...
if (x == null) {
   println("Wrong number format in arg1: '$arg1'")
   return
}

if (y == null) {
   println("Wrong number format in arg2: '$arg2'")
   return
}

println(x * y)

المزيد عن القيمة الفارغة null.

التحقُّق من النوع والتحويلات التلقائية بين الأنواع (Casting)

يُستخدَم المعامل is للتحقق من أنّ قيمة التعبير تتبع لأحد الأنواع، وإذا ما تمّ التحقُّق من القيمة الثابتة للمتغيّرات المحليّة (immutable local variable) أو من قيمة إحدى الخاصّيات فلا حاجة للتصريح عن تحويلها (casting)، كما هو واضح بالشيفرة الآتية:

```
fun getStringLength(obj: Any): Int? {
   if (obj is String) {
       //‎‎سيُحوّل الكائن تلقائيًا في هذا الجزء إلى نوع String
       return obj.length
   }

   //‎‎وفي هذا الجزء سيبقى من النوع Any
   return null
}

أو بالشكل:

fun getStringLength(obj: Any): Int? {
   if (obj !is String) return null
   //‎‎سيُحوّل الكائن obj تلقائيًا إلى نوع String في هذا الجزء
   return obj.length
}

أو بشكل آخر:

fun getStringLength(obj: Any): Int? {
   //‎‎سيُحوّل الكائن الموجود على يمين المعامل تلقائيًا إلى نوع String
   if (obj is String && obj.length > 0) {
       return obj.length
   }
   return null
}

المزيد عن الأصناف classes والتحويل ما بين الأنواع type casts.

استخدام حلقة for

تُكتب حلقة for بالشكل الآتي:

val items = listOf("apple", "banana", "kiwi")

for (item in items) {
   println(item)
}

إذ يُستخدَم المعامل in للمرور بكافة العناصر الموجودة في المجموعة items. ويوضح المثال الآتي استخدام حلقة for أيضًا ولكن مع الأدلة (indices) للعناصر بدلًا من العناصر نفسها:

val items = listOf("apple", "banana", "kiwi")

for (index in items.indices) {
   println("item at $index is ${items[index]}")
}

المزيد عن حلقة for.

استخدام حلقة while

يكون استخدامها وفق الصيغة الآتية:

val items = listOf("apple", "banana", "kiwi")
var index = 0
while (index < items.size) {
   println("item at $index is ${items[index]}")
   index++
}

حيث ستستمر الحلقة لحين الوصول إلى آخر عنصر في المجموعة (بلوغ المتحول index قيمة دليل العنصر الأخير).

المزيد عن حلقة while.

استخدام التعبير when

يُستخدَم التعبير when للتحقق من قيمة أو إحدى خاصّيات الكائن أو المتحوِّل بالشكل الآتي:

fun describe(obj: Any): String =
when (obj) {
   1          -> "One"
   "Hello"    -> "Greeting"
   is Long    -> "Long"
   !is String -> "Not a string"
   else       -> "Unknown"
}

المزيد عن التعبير when.

استخدام المجالات (Ranges)

يُستخَدم المعامل in للتأكد من وقوع قيمةٍ ما ضمن مجالٍ مُحدَّد، وهذا يتَّضح عبر المثال الآتي:

val x = 10
val y = 9

if (x in 1..y+1) { // المجال يمتد من قيمة 1 وحتى قيمة y+1
   println("fits in range")
}

كما ويمكن استخدامه للتحقُّق من أنّ القيمة خارج هذا المجال، بالشكل الآتي:

val list = listOf("a", "b", "c")

if (-1 !in 0..list.lastIndex) { // المجال يمتد من 0 وحتى قيمة الدليل الأخير
   println("-1 is out of range")
}

if (list.size !in list.indices) {
   println("list size is out of valid list indices range too")
}

ولتكرار عددٍ من التعليمات في مجالٍ مُحدَّدٍ، تصبح الصياغة بالشكل:

for (x in 1..5) { // المجال يمتد من 1 حتى 5
   print(x)
}

أما للتكرار المتسلسل وفق خطوة step:

for (x in 1..10 step 2) { //يبدأ المجال من القيمة 1 وينتهي بالقيمة 10 وبخطوةٍ مقدارها 2
   print(x)
}

println()

for (x in 9 downTo 0 step 3) { // يبدأ المجال بالقيمة 9 وينتهي بشكل تنازليّ عند القيمة 0 بخطوةٍ مقدارها 3
   print(x)
}

المزيد عن المجالات ranges.

استخدام المجموعات (Collections)

يُمكن تنفيذ التكرار على العناصر الموجودة في المجموعة بالشكل الآتي:

for (item in items) {
   println(item)
}

أما للتأكُّد من وجود عنصرٍ ما ضمن المجموعة باستخدام المعامل in تصبح الصياغة بالشكل الآتي:

when {
   "orange" in items -> println("juicy")
   "apple" in items -> println("apple is fine too")
}

كما ويمكن استخدام تعابير lambda لتصنيف وتعيين المجموعات، كما في الشيفرة الآتية:

fruits
.filter { it.startsWith("a") }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { println(it) }

المزيد عن الدوال عالية المستوى وتعابير lambdas.

إنشاء الأصناف (Classes) والكائنات الناتجة عنها (Objects)

يُوضِّح المثال الآتي إنشاء كائنَين من صنفَين مختلفين:

val rectangle = Rectangle(5.0, 2.0) // لا حاجة لكلمة new

val triangle = Triangle(3.0, 4.0, 5.0)

المزيد عن الأصناف classes والكائنات objects.

مصادر