0Pricing
Scala for Backend Engineering & Functional Programming · درس

الأنواع العامة ومعاملات الأنواع

افهم كيفية كتابة فئات ودوال عامة لإنشاء مكونات قابلة لإعادة الاستخدام مع أمان الأنواع

الأنواع العامة ومعاملات الأنواع درس مجاني في Scala for Backend Engineering & Functional Programming على CoddyKit. هذا هو الدرس 1 من أصل 3. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Scala for Backend Engineering & Functional Programming، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Scala for Backend Engineering & Functional Programming 3 دروس في المجموع.

ما المقصود بالأنواع العامة؟

تخيلوا أنكم بحاجة إلى إنشاء حاوية يمكنها الاحتفاظ بأي نوع من العناصر، مثل String أو Int أو كائن مخصص. من دون الأنواع العامة، سيتعين عليكم إما كتابة حاوية منفصلة لكل نوع، مما يؤدي إلى تكرار الشيفرة، أو استخدام نوع واسع جدًا مثل Any.

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

فخ النوع `Any`

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

جرّبوا تشغيل هذا المثال وفكروا في المشكلات المحتملة:

class ItemHolder(val item: Any)

object Main {
  def main(args: Array[String]): Unit = {
    val stringHolder = new ItemHolder("Hello Scala")
    val numberHolder = new ItemHolder(123)

    // We need to cast, and it's unsafe if we get it wrong
    val retrievedString = stringHolder.item.asInstanceOf[String]
    println(s"Retrieved String: $retrievedString")

    // If we uncomment the line below, it compiles but will crash at runtime!
    // val wrongType = numberHolder.item.asInstanceOf[String]
    // println(s"Wrong Type: $wrongType")
  }
}

التعريف بمعاملات الأنواع

تحل الأنواع العامة مشكلة نوع Any من خلال تقديم معاملات الأنواع. وهي أشبه بعناصر نائبة للأنواع الفعلية التي تحددونها عند إنشاء نسخة من فئة أو استدعاء أسلوب.

في Scala، تُعرَّف معاملات الأنواع باستخدام الأقواس المربعة ([]) بعد اسم الفئة أو الأسلوب، وعادةً ما تُستخدم أحرف إنجليزية كبيرة مفردة مثل T وA وB وغيرها. على سبيل المثال، يعلن class Box[T] عن فئة عامة باسم Box تأخذ معامل نوع واحدًا هو T.

بناء Box عامة

لننشئ فئة عامة بسيطة باسم Box يمكنها الاحتفاظ بأي نوع من المحتوى. لاحظوا كيف يُستخدم T في جميع أنحاء تعريف الفئة لتمثيل نوع المحتوى.

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

class Box[T](val content: T) {
  def get: T = content
  override def toString: String = s"Box($content)"
}

object Main {
  def main(args: Array[String]): Unit = {
    val stringBox = new Box("Scala is fun!")
    val intBox = new Box(42)
    val booleanBox = new Box(true)

    println(stringBox)
    println(intBox)
    println(booleanBox)
  }
}

سلامة الأنواع باستخدام الأنواع العامة

أكبر ميزة للأنواع العامة هي سلامة الأنواع أثناء الترجمة. عند إنشاء Box[String]، يعرف المترجم أنها يجب أن تحتوي على سلاسل نصية فقط. وإذا حاولتم وضع عدد صحيح فيها أو استرجاع محتواها كعدد صحيح، فسوف يكتشف المترجم الخطأ فورًا.

يمنع ذلك العديد من أخطاء وقت التشغيل الشائعة التي تحدث مع الأساليب الأقل أمانًا من ناحية الأنواع، مثل استخدام Any.

class Box[T](val content: T) {
  def get: T = content
}

object Main {
  def main(args: Array[String]): Unit = {
    val myStringBox: Box[String] = new Box("CoddyKit")
    val myIntBox: Box[Int] = new Box(100)

    // This works, type is String
    val s: String = myStringBox.get
    println(s"String from box: $s")

    // This works, type is Int
    val i: Int = myIntBox.get
    println(s"Int from box: $i")

    // The compiler prevents this line from compiling:
    // val wrong: String = myIntBox.get // Type mismatch error!
  }
}

الأنواع العامة للأساليب أيضًا!

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

يفيد ذلك عندما تكون لديكم عملية محددة يمكن تطبيقها على أنواع متعددة، بينما الفئة نفسها ليست حاوية عامة.

أسلوب `printPair` المرن

إليكم مثالًا على أسلوب عام هو printPair. يأخذ هذا الأسلوب معاملي نوع، هما A وB، مما يتيح له طباعة زوج مكوّن من أي نوعين. لاحظوا كيف تُعلن معاملات الأنواع مباشرة بعد اسم الأسلوب.

يجعل ذلك الأسلوب قابلًا لإعادة الاستخدام بدرجة كبيرة مع مجموعات مختلفة من البيانات.

object Util {
  def printPair[A, B](first: A, second: B): Unit = {
    println(s"Pair: ($first, $second)")
    println(s"Type of first: ${first.getClass.getName}")
    println(s"Type of second: ${second.getClass.getName}")
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    Util.printPair("Hello", 123)
    Util.printPair(true, List(1, 2, 3))
    Util.printPair(4.5, 'C')
  }
}

استنتاج الأنواع الذكي

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

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

class Container[T](val item: T) {
  def getItem: T = item
}

object Main {
  def main(args: Array[String]): Unit = {
    // Scala infers T as String
    val stringContainer = new Container("Inferred String")
    println(s"Content: ${stringContainer.getItem}, Type: ${stringContainer.getItem.getClass.getName}")

    // Scala infers T as Double
    val doubleContainer = new Container(3.14)
    println(s"Content: ${doubleContainer.getItem}, Type: ${doubleContainer.getItem.getClass.getName}")

    // You can also specify explicitly, but often not needed
    val explicitIntContainer: Container[Int] = new Container(500)
    println(s"Content: ${explicitIntContainer.getItem}, Type: ${explicitIntContainer.getItem.getClass.getName}")
  }
}

تقييد الأنواع العامة باستخدام الحدود

قد ترغبون أحيانًا في أن تعمل فئة أو أسلوب عام فقط مع الأنواع التي تمتلك قدرات معينة. فعلى سبيل المثال، يجب ألا يقبل أسلوب يجمع الأعداد إلا الأنواع العددية.

تتيح حدود الأنواع تقييد الأنواع التي يمكن استخدامها كمعاملات للأنواع. ويعني الحد العلوي (<:) أن معامل النوع يجب أن يكون نوعًا فرعيًا من نوع محدد. على سبيل المثال، يعني [T <: Animal] أن T يجب أن يكون Animal أو نوعًا فرعيًا منه.

class Animal { def speak(): String = "..." }
class Dog extends Animal { override def speak(): String = "Woof!" }
class Cat extends Animal { override def speak(): String = "Meow!" }

// This box can only hold types that are Animal or its subtypes
class AnimalShelter[T <: Animal](val animal: T) {
  def getAnimalSound: String = animal.speak()
}

object Main {
  def main(args: Array[String]): Unit = {
    val dogShelter = new AnimalShelter(new Dog())
    println(s"Dog says: ${dogShelter.getAnimalSound}")

    val catShelter = new AnimalShelter(new Cat())
    println(s"Cat says: ${catShelter.getAnimalSound}")

    // This would NOT compile because Car is not an Animal:
    // class Car
    // val carShelter = new AnimalShelter(new Car()) 
  }
}

تحدي الفئة العامة

حان الوقت لاختبار فهمكم للأنواع العامة!

اقرؤوا العبارات التالية بعناية، وحددوا جميع العبارات الصحيحة المتعلقة بالفئات والأساليب العامة في Scala.

الأنواع العامة: أهم النقاط

استكشفنا في هذا الدرس الأنواع العامة في Scala، وهي ميزة قوية لكتابة شيفرة مرنة وآمنة من ناحية الأنواع.

  • تتيح الأنواع العامة للفئات والأساليب العمل مع أنواع مختلفة.
  • تستخدم معاملات الأنواع (مثل [T]) كعناصر نائبة للأنواع الفعلية.
  • توفر الأنواع العامة سلامة الأنواع أثناء الترجمة، مما يمنع أخطاء وقت التشغيل.
  • غالبًا ما يجعل استنتاج الأنواع في Scala الشيفرة العامة موجزة.
  • يمكن أن تقيّد حدود الأنواع (مثل <:) معاملات الأنواع بأنواع فرعية محددة.

باستخدام الأنواع العامة، يمكنكم كتابة مكونات قابلة لإعادة الاستخدام، ومتينة وقابلة للتكيف مع أنواع بيانات مختلفة، مما يجعل شيفرة Scala الخاصة بكم أقوى وأقل عرضة للأخطاء.

الأسئلة الشائعة

هل درس «الأنواع العامة ومعاملات الأنواع» مجاني؟

نعم — نص درس «الأنواع العامة ومعاملات الأنواع» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Scala for Backend Engineering & Functional Programming، انتقل إلى CoddyKit PRO. تتضمن دورة Scala for Backend Engineering & Functional Programming 3 دروس في المجموع.

ماذا ستتعلم في «الأنواع العامة ومعاملات الأنواع»؟

افهم كيفية كتابة فئات ودوال عامة لإنشاء مكونات قابلة لإعادة الاستخدام مع أمان الأنواع تتمرن على Scala for Backend Engineering & Functional Programming مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Scala for Backend Engineering & Functional Programming؟

لا تُشترط خبرة سابقة. Scala for Backend Engineering & Functional Programming على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 3.

كم من الوقت يستغرق درس «الأنواع العامة ومعاملات الأنواع»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Scala for Backend Engineering & Functional Programming هذا؟

نعم. كل درس في Scala for Backend Engineering & Functional Programming يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الأنواع العامة ومعاملات الأنواع
  2. التباين: التغاير والتباين العكسي
  3. فئات الأنواع وImplicits
← العودة إلى Scala for Backend Engineering & Functional Programming