
مرحبا ، habrozhiteli! لدينا كتاب تم نشره لدراسة Kotlin باستخدام تقنية Head First ، والتي تتجاوز بناء الجملة والإرشادات الخاصة بحل مشاكل معينة. يمنحك هذا الكتاب كل ما تحتاجه - من أساسيات اللغة إلى الأساليب المتقدمة. ويمكنك ممارسة البرمجة الموجهة للكائنات والوظيفية.
ويرد مقتطف "فئات البيانات" تحت الخفض.
العمل مع البيانات
لا أحد يريد إضاعة الوقت وإعادة ما تم فعله بالفعل. تستخدم معظم التطبيقات فئات لتخزين البيانات. لتبسيط العمل ، اقترح مبتكرو Kotlin مفهوم فئة البيانات. في هذا الفصل ، سوف تتعلم كيف تساعدك فئات البيانات في كتابة رمز أكثر أناقة وإيجازًا يمكن أن تحلم به من قبل فقط. سننظر في وظائف المساعد لفئات البيانات ونتعرف على كيفية تحليل كائن البيانات إلى مكونات. في نفس الوقت ، سنصف كيف تجعل قيم المعلمة الافتراضية الشفرة أكثر مرونة ، كما نقدم لك أيًا ، الجد لجميع الفئات الفائقة.
يستدعي العامل == دالة تسمى يساوي
كما تعلمون بالفعل ، يمكن استخدام عامل التشغيل == للتحقق من المساواة. في كل مرة يتم تنفيذ العبارة == ، يتم استدعاء دالة تسمى يساوي. يحتوي كل كائن على دالة يساوي ، ويحدد تنفيذ هذه الوظيفة سلوك العامل ==.
بشكل افتراضي ، تقوم الدالة equals للتحقق من المساواة بالتحقق مما إذا كان هناك مرجعان متغيران لنفس الكائن.
لفهم كيف يعمل ، تخيل متغيرين الذئب اسمه w1 و w2. إذا كان w1 و w2 يحتويان على إشارات إلى كائن Wolf واحد ، عند مقارنتهما بعامل التشغيل == ، تكون النتيجة صحيحة:
ولكن إذا احتوت w1 و w2 على إشارات إلى كائنات Wolf مختلفة ، فإن مقارنتها بعامل == تعطي النتيجة خاطئة ، حتى إذا كانت الكائنات تحتوي على نفس قيم الخصائص.
كما ذكرنا سابقًا ، يتم تضمين وظيفة المساواة تلقائيًا في كل كائن تقوم بإنشائه. ولكن من أين تأتي هذه الوظيفة؟
يساوي يرث من الطبقة الفائقة أي
يحتوي كل كائن على دالة تسمى يساوي لأن فئته ترث دالة من فئة تسمى أي. أي فئة هي الجد لجميع الطبقات: الطبقة الفائقة الناتجة عن كل شيء. كل فئة تحددها هي فئة فرعية من الفئة "أي" ، ولا تحتاج إلى الإشارة إلى ذلك في البرنامج. وبالتالي ، إذا قمت بكتابة رمز فئة يسمى myClass ، والذي يبدو كالتالي:
class MyClass { ... }
سيقوم المحول البرمجي تلقائيًا بتحويله إلى النموذج التالي:
كل فئة هي فئة فرعية من أي ويرث سلوكها. كل فصل هو فئة فرعية من أي ، وليس لديك للإبلاغ عن هذا في البرنامج.
أهمية أي ميراث
بما في ذلك أي كما أن الطبقة الفائقة الناتجة لها ميزتان مهمتان:
- إنه يضمن أن كل صف يرث السلوك الشائع. يعرّف أي فئة السلوك المهم الذي تعتمد عليه عملية النظام. ولأن كل فئة هي فئة فرعية من أي ، هذا السلوك موروث من قبل جميع الكائنات التي تنشئها. لذلك ، تُعرّف أي فئة دالة تُسمى يساوي ، وبالتالي ، فإن هذه الوظيفة يتم توارثها تلقائيًا بواسطة جميع الكائنات.
- وهذا يعني أن تعدد الأشكال يمكن استخدامها مع أي كائنات. كل فئة هي فئة فرعية من أي ، لذلك فإن أي كائن تقوم بإنشائه لديه أي فئة كنوعها النهائي الفائق. هذا يعني أنه يمكنك إنشاء وظيفة باستخدام أي معلمات أو أي نوع إرجاع يعمل مع كائنات من أي نوع. هذا يعني أيضًا أنه يمكنك إنشاء صفائف متعددة الأشكال لتخزين كائنات من أي نوع مع رمز النموذج التالي:
val myArray = arrayOf(Car(), Guitar(), Giraffe())
يلاحظ المحول البرمجي أن كل كائن في الصفيف لديه نموذج أولي شائع لـ Any ، وبالتالي ينشئ صفيفًا من النوع Array.
السلوك العام الموروث من قبل أي فئة يستحق نظرة فاحصة.
السلوك الشائع الموروث من أي
يعرّف أي فئة العديد من الوظائف الموروثة من كل فئة. فيما يلي أمثلة على الوظائف الأساسية وسلوكها:
- يساوي (أي: أي): منطقية
يتم التحقق مما إذا كان يتم اعتبار كائنين "متساوين". بشكل افتراضي ، ترجع الدالة true إذا تم استخدامها للتحقق من كائن واحد ، أو false - لكائنات مختلفة. وراء الكواليس ، يتم استدعاء وظيفة المساواة في كل مرة يتم فيها استخدام عامل التشغيل == في البرنامج.
val w1 = Wolf() val w1 = Wolf() val w2 = Wolf() val w2 = w1 println(w1.equals(w2)) println(w1.equals(w2)) false (equals false, true (equals true, w1 w2 w1 w2 .) — , w1 == w2.
- hashCode (): كثافة العمليات
إرجاع رمز تجزئة لكائن. غالبًا ما تستخدم رموز تجزئة البيانات من قبل بعض هياكل البيانات لتخزين واسترداد القيم بكفاءة.
val w = Wolf() println(w.hashCode())
523429237 (قيمة شفرة التجزئة w)
- toString (): سلسلة
إرجاع رسالة سلسلة تمثل الكائن. بشكل افتراضي ، تحتوي الرسالة على اسم الفصل ورقم ، وهو ما لا نهتم به عادة.
val w = Wolf() println(w.toString())
الذئب @ 1f32e575بشكل افتراضي ، تتحقق الدالة equals مما إذا كان كائنين هما نفس الكائن الفعلي.
تحدد الدالة equals سلوك العامل ==.
يوفر أي فئة تطبيقًا افتراضيًا لجميع الوظائف المدرجة ، وتورث هذه التطبيقات جميع الفئات. ومع ذلك ، يمكنك تجاوز هذه التطبيقات لتغيير السلوك الافتراضي لجميع الوظائف المدرجة.
تحقق معادلة بسيطة لكائنين
في بعض الحالات ، تحتاج إلى تغيير تطبيق الدالة يساوي لتغيير سلوك العامل ==.
افترض أن لديك فئة وصفة تسمح لك بإنشاء كائنات لتخزين الوصفات. في هذه الحالة ، من المحتمل أن تفكر في كائنين Recipe (أو ما يعادلها) إذا كانا يحتويان على وصف لنفس الوصفة. لنفترض أن فئة الوصفة محددة بخاصيتين - العنوان و isVegetarian:
class Recipe(val title: String, val isVegetarian: Boolean) { }
سيعود العامل == صحيحًا إذا تم استخدامه لمقارنة كائنين Recipe بنفس الخصائص والعنوان و isVegetarian:
val r1 = Recipe("Chicken Bhuna", false) val r2 = Recipe("Chicken Bhuna", false)
على الرغم من أنه يمكنك تغيير سلوك العامل == عن طريق كتابة تعليمة برمجية إضافية لتجاوز دالة يساوي ، إلا أن مطوري Kotlin قدموا حلاً أكثر ملاءمة: لقد ابتكروا مفهوم فئة البيانات. دعونا نرى ما هي هذه الفئات وكيف يتم إنشاؤها.
فئة البيانات تسمح لك بإنشاء كائنات البيانات.
فئة البيانات هي فئة لإنشاء كائنات لتخزين البيانات. يتضمن أدوات مفيدة للعمل مع البيانات - على سبيل المثال ، تطبيق جديد لوظيفة يساوي ، والذي يتحقق ما إذا كان كائنين من البيانات تحتوي على قيم الخاصية نفسها. إذا احتوى كيانان على نفس البيانات ، فيمكن اعتبارهما متساوين.
لتعريف فئة بيانات ، تسبق تعريف البيانات المعتاد باستخدام الكلمة الأساسية للبيانات. تقوم التعليمة البرمجية التالية بتحويل فئة Recipe التي تم إنشاؤها مسبقًا إلى فئة بيانات:
data class Recipe(val title: String, val isVegetarian: Boolean) { }
بادئة البيانات تحول فئة عادية إلى فئة بيانات.
كيفية إنشاء كائنات تستند إلى فئة البيانات
يتم إنشاء كائنات فئة البيانات بنفس طريقة كائنات الفئة العادية: عن طريق استدعاء مُنشئ هذه الفئة. على سبيل المثال ، تقوم التعليمة البرمجية التالية بإنشاء كائن بيانات وصفة جديد وتعيينه إلى متغير جديد باسم r1:
val r1 = Recipe("Chicken Bhuna", false)
تتجاوز فئات البيانات تلقائيًا دالاتها متساوية لتغيير سلوك العامل == بحيث يتم التحقق من تكافؤ الكائنات استنادًا إلى قيم خصائص كل كائن. على سبيل المثال ، إذا قمت بإنشاء كائنين Recipe لهما نفس قيم الخصائص ، فإن مقارنة الكائنين مع = = عامل التشغيل ستعطي النتيجة صحيحة ، لأن نفس البيانات مخزنة فيها:
val r1 = Recipe("Chicken Bhuna", false) val r2 = Recipe("Chicken Bhuna", false)
تعتبر r1 و r2 "متساوية" لأن كائنين Recipe يحتويان على نفس البيانات.
بالإضافة إلى التطبيق الجديد لوظيفة يساوي الموروثة من فئات البيانات الفائقة Any ، فئة
أيضا تجاوز وظائف hashCode و toString. دعونا نرى كيف يتم تنفيذ هذه الوظائف.
كائنات فئة إعادة تعريف السلوك الموروث
للعمل مع البيانات ، يحتاج فئة البيانات إلى كائنات ، وبالتالي فإنه يوفر تلقائيًا التطبيقات التالية للتساوي ، ووظائف hashCode و toString الموروثة من أي فئة فائقة:
يقارن الدالة يساوي قيم الخاصية
عند تعريف فئة بيانات ، فإن دالة تساويها (وبالتالي = = عامل التشغيل) لا تزال تُرجع بشكل صحيح إذا كانت الروابط تشير إلى نفس الكائن. ولكنه يعود أيضًا إذا كانت الكائنات لها نفس قيم الخصائص المعرفة في المُنشئ:
val r1 = Recipe("Chicken Bhuna", false) val r2 = Recipe("Chicken Bhuna", false) println(r1.equals(r2)) true
تعتبر كائنات البيانات متساوية إذا كانت خصائصها تحتوي على نفس القيمة.
لكائنات متساوية ، يتم إرجاع نفس القيم hashCode
إذا تم اعتبار كائنين متماثلين (بمعنى آخر ، لهما نفس قيم الخصائص) ، فإن دالة hashCode تُرجع نفس القيمة لهذه الكائنات:
val r1 = Recipe("Chicken Bhuna", false) val r2 = Recipe("Chicken Bhuna", false) println(r1.hashCode()) println(r2.hashCode())
241131113
241131113تُرجع الدالة toString قيم كل الخصائص
أخيرًا ، لم تعد دالة toString تُرجع اسم الفئة ، متبوعة برقم ، ولكنها تُرجع سلسلة مفيدة تحتوي على قيم جميع الخصائص المحددة في مُنشئ فئة البيانات:
val r1 = Recipe("Chicken Bhuna", false) println(r1.toString()) Recipe(title=Chicken Bhuna, isVegetarian=false)
بالإضافة إلى تجاوز وظائف الموروثة من أي فئة فائقة ، توفر فئة البيانات أيضا أدوات إضافية توفر المزيد من العمل الفعال مع البيانات ، على سبيل المثال ، القدرة على نسخ كائنات البيانات. دعونا نرى كيف تعمل هذه الأدوات.
نسخ كائنات البيانات باستخدام وظيفة النسخ
إذا كنت بحاجة إلى إنشاء نسخة من كائن البيانات عن طريق تغيير بعض خصائصه ، مع ترك الخصائص الأخرى في حالتها الأصلية ، استخدم وظيفة النسخ. للقيام بذلك ، يتم استدعاء الوظيفة للكائن الذي تريد نسخه ، ويتم تمرير أسماء جميع الخصائص القابلة للتغيير ذات القيم الجديدة إليها.
افترض أن لديك كائن وصفة باسم r1 ، والذي تم تعريفه في الكود مثل هذا:
val r1 = Recipe("Thai Curry", false)
إذا كنت ترغب في إنشاء نسخة من كائن Recipe ، واستبدال قيمة الخاصية isVegetarian بالقيمة true ، يتم ذلك كما يلي:
في الجوهر ، هذا يعني "إنشاء نسخة من الكائن r1 ، وتغيير قيمة الخاصية isVegetarian الخاصة به إلى true ، وتعيين كائن جديد لمتغير يسمى r2." يؤدي هذا إلى إنشاء نسخة جديدة من الكائن ، ويظل الكائن الأصلي على حاله.
بالإضافة إلى وظيفة النسخ ، توفر فئات البيانات أيضًا مجموعة من الوظائف لتقسيم كائن بيانات إلى مجموعة من قيم خصائصه - تسمى هذه العملية التدمير. دعونا نرى كيف يتم ذلك.
فئات البيانات تحدد مكونات المكون ...
عند تعريف فئة بيانات ، يضيف المترجم تلقائيًا إلى الفصل مجموعة من الوظائف التي يمكن استخدامها كآلية بديلة للوصول إلى قيم خصائص الكائن. هذه الوظائف معروفة تحت الاسم العام لوظائف componentN ، حيث N هي عدد الخصائص التي سيتم استخراجها (بترتيب الإعلان).
لمعرفة كيفية عمل وظائف componentN ، افترض أن لديك كائن الوصفة التالي:
val r = Recipe("Chicken Bhuna", false)
إذا كنت ترغب في الحصول على قيمة الخاصية الأولى للكائن (خاصية العنوان) ، يمكنك استدعاء دالة component1 () للكائن لهذا:
val title = r.component1()
المكون 1 () بإرجاع المرجع الموجود في الخاصية الأولى المعرفة في مُنشئ فئة البيانات.
تعمل الوظيفة بنفس الكود التالي:
val title = r.title
الكود مع الوظيفة أكثر عالمية. لماذا تعد وظائف ComponentN مفيدة جدًا في فئات البيانات؟
... مصممة لإعادة هيكلة كائنات البيانات
دالات componentN العامة مفيدة في المقام الأول لأنها توفر طريقة بسيطة وملائمة لتقسيم كائن بيانات إلى قيم خصائص ، أو إتلافه.
افترض أنك تريد أن تأخذ قيم خصائص كائن وصفة وتعيين قيمة كل من خصائصه إلى متغير منفصل. بدلا من الرمز
val title = r.title val vegetarian = r.isVegetarian
مع المعالجة المتسلسلة لكل خاصية ، يمكنك استخدام الكود التالي:
val (title, vegetarian) = r
يعين سند الملكية الأولى r و نباتي للملكية الثانية.
هذا الرمز يعني "إنشاء متغيرين ، العنوان والنباتي ، وتعيين قيمة أحد خصائص r لكل متغير." يفعل نفس الشظية التالية
val title = r.component1() val vegetarian = r.component2()
ولكن اتضح أكثر إحكاما.
يتحقق العامل === دائمًا من أن متغيرين يشيران إلى نفس الكائن.إذا كنت تريد التحقق مما إذا كان هناك متغيرين يشيران إلى نفس الكائن بغض النظر عن نوعهما ، فاستخدم عامل التشغيل === بدلاً من ==. يعطي العامل === النتيجة صحيحة إذا (و فقط إذا) عندما يحتوي متغيرين على إشارة إلى كائن حقيقي واحد. إذا كان لديك متغيرين ، x و y ، والتعبير التالي:
x === y
يعطي النتيجة صحيحة ، فأنت تعلم أن المتغيرات x و y يجب أن تشير إلى نفس الكائن.
بخلاف العامل == ، يكون سلوك العامل === مستقلًا عن دالة يساوي. يتصرف عامل التشغيل === دائمًا كما هو بصرف النظر عن نوع الفئة.
الآن بعد أن تعلمت كيفية إنشاء فئات البيانات واستخدامها ، قم بإنشاء مشروع لرمز الوصفة.
إنشاء مشروع وصفات
قم بإنشاء مشروع Kotlin جديد لـ JVM واسمه "وصفات". ثم إنشاء جديد
ملف Kotlin المسمى Recipes.kt: حدد مجلد src ، افتح قائمة "ملف" وحدد الأمر
جديد → Kotlin ملف / الدرجة. أدخل اسم الملف "Recipes" وحدد خيار File في المجموعة Kind.
نضيف فئة بيانات جديدة إلى المشروع المسمى Recipe وإنشاء كائنات بيانات Recipe. أدناه هو الرمز. حدّث نسختك من Recipes.kt وجعلها متوافقة مع إصدارنا:
data class Recipe(val title: String, val isVegetarian: Boolean) ( {} , .) fun main(args: Array<String>) { val r1 = Recipe("Thai Curry", false) val r2 = Recipe("Thai Curry", false) val r3 = r1.copy(title = "Chicken Bhuna") ( r1 title) println("r1 hash code: ${r1.hashCode()}") println("r2 hash code: ${r2.hashCode()}") println("r3 hash code: ${r3.hashCode()}") println("r1 toString: ${r1.toString()}") println("r1 == r2? ${r1 == r2}") println("r1 === r2? ${r1 === r2}") println("r1 == r3? ${r1 == r3}") val (title, vegetarian) = r1 ( r1) println("title is $title and vegetarian is $vegetarian") }
عند تشغيل التعليمات البرمجية ، سيظهر النص التالي في نافذة الإخراج IDE:
r1 hash code: -135497891 r2 hash code: -135497891 r3 hash code: 241131113 r1 toString: Recipe(title=Thai Curry, isVegetarian=false) r1 == r2? true r1 === r2? false r1 == r3? false title is Thai Curry and vegetarian is false
»يمكن الاطلاع على مزيد من المعلومات حول الكتاب على
موقع الناشر»
المحتويات»
مقتطفات
خصم 25 ٪ على كوبون Khabrozhitel -
Kotlinعند دفع النسخة الورقية من الكتاب ، يتم إرسال كتاب إلكتروني عبر البريد الإلكتروني.