English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
النوع العام، أو "نوع بيانات م参数يز"، يجعل النوع م参数يز، يمكن استخدامه في الفئات، الواجهات، والأساليب.
تمثل Kotlin العامة مثل Java، وتوفر أمانًا نوعيًا وتزيل قلق التحويلات النوعية.
إعلان فئة عامة
class Box<T>(t: T) { var value = t }
عند إنشاء مثال على الفئة، يجب تحديد متغير نوع البيانات:
val box: Box<Int> = Box<Int>(1) // أو val box = Box(1) // يتم التنبؤ بالنوع من قبل المترجم، 1 نوع Int، لذا يعرف المترجم أننا نقول Box<Int>.
مثال على إدخال بيانات نوعية في فئة Box النوعية:
class Box<T>(t: T) { var value = t } fun main(args: Array<String>) { var boxInt = Box<Int>(10) var boxString = Box<String>("w3codebox") println(boxInt.value) println(boxString.value) }
النتيجة هي:
10 w3codebox
يمكن تعريف متغير النوع العام، يمكن كتابة نوع البديل بشكل كامل، وإذا كان يمكن للمرجع التنبؤ بالنوع، يمكن التغاضي عن نوع البديل.
بيان دالة Kotlin العامة مشابه لـ Java، ويجب وضع متغير نوع البيانات قبل اسم الدالة:
fun <T> boxIn(value: T) = Box(value) // كل مما يلي هو جملة صالحة val box4 = boxIn<Int>(1) val box5 = boxIn(1) // يتم التنبؤ بالنوع من قبل المترجم
في وقت التعبير عن الدالة العامة، إذا كان يمكن التنبؤ بمتغير نوع البيانات، يمكن التغاضي عن متغير نوع البيانات.
مثال على إنشاء دالة عامة doPrintln، حيث تقوم الدالة بمعالجة أنواع البيانات المختلفة بشكل مناسب:
fun main(args: Array<String>) { val age = 23 val name = "w3codebox" val bool = true doPrintln(age) // نوع بيانات الصحيح doPrintln(name) // نص doPrintln(bool) // نوع بيانات الحق أو الخطأ } fun <T> doPrintln(content: T) { when (content) { is Int -> println("رقم صحيح هو $content") is String -> println("تحويل النص إلى حروف كبيرة: ${content.toUpperCase()}") else -> println("T ليس نوعًا كاملاً ولا هو نص") } }
النتيجة هي:
رقم كامل هو 23 تحويل النص إلى حروف كبيرة: w3codebox T ليس نوعًا كاملاً ولا هو نص
يمكننا استخدام قيود الجينات العليا لتحديد الأنواع التي يُسمح استخدامها للمعلمة المحددة.
يستخدم Kotlin : لتقييد أنواع الجينات العليا.
أكثر القيود شيوعًا هي الحد الأعلى (upper bound):
fun <T : Comparable<T>> sort(list: List<T>) { // …… }
يمكن استبدال فرع Comparable بالنوع T. على سبيل المثال:
sort(listOf(1, 2, 3)) // حسن. Int هو فرع لـ Comparable<Int> sort(listOf(HashMap<Int, String>())) // خطأ: HashMap<Int, String> ليس فرعًا لـ Comparable<HashMap<Int, String>>
الحد الأعلى الافتراضي هو Any?.
للمزيد من شروط الحدود يمكن استخدام جملة where:
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String> حيث T : CharSequence, T : Comparable<T> { return list.filter { it > threshold }.map { it.toString() } }
ليست هناك أنواع غير محددة في Kotlin، بل هناك شيئان آخران: التباين في المكان التعبيري (declaration-site variance) وتصوير الأنواع (type projections).
استخدام التباين في المكان التعبيري باستخدام وسم التباين المتباين: in،out،المستهلك in،المقدم out.
استخدام out يجعل معرف المعلمة المتباينة، المعلمة المتباينة يمكن استخدامها كنوع للإرجاع ولكن لا يمكن استخدامها كنوع للإدخال:
// تعريف فئة تدعم التباين class w3codebox<out A>(val a: A) { fun foo(): A { return a } } fun main(args: Array<String>) { var strCo: w3codebox<String> = w3codebox("a") var anyCo: w3codebox<Any> = w3codebox<Any>("b") anyCo = strCo println(anyCo.foo()) // يُطبع a }
in يجعل معلمة النوع تباينية، معلمة النوع التباينية يمكن استخدامها كنوع إدخال فقط، ولا يمكن استخدامها كنوع مُخرج:
// تعريف فئة تدعم التباين العكسي class w3codebox<in A>(a: A) { fun foo(a: A) { } } fun main(args: Array<String>) { var strDCo = w3codebox("a") var anyDCo = w3codebox<Any>("b") strDCo = anyDCo }
في بعض الأحيان، قد تريد أن تعبر عن عدم معرفتك لأي معلومات حول معلمة النوع، ولكنك لا تزال ترغب في استخدامها بأمان. "الاستخدام بأمان" هنا يعني تعريف تدفق نوعي لنوع عام، يتطلب من جميع الأمثلة على هذا النوع أن تكون فرعية لهذا التدفق.
للإجابة على هذا السؤال، يقدم Kotlin لغة كتابة تُدعى نجمية التدفق (star-projection):
إذا تم تعريف النوع كـFoo<out T>، حيث T هو معلمة نوع متباينة، الحد الأعلى (upper bound) هو TUpper، Foo<> يُشابه Foo<out TUpper>. إنه يعني، عندما يكون T غير معروف، يمكنك إدخال بأمان من Foo<> قراءة القيم من نوع TUpper.
إذا تم تعريف النوع كـFoo<in T>، حيث T هو معلمة نوع متباينة، Foo<> يُشابه Foo<inNothing>. إنه يعني، عندما يكون T غير معروف، لا يمكنك إدخال بأمان إلى Foo<> إدخال أي شيء.
إذا تم تعريف النوع كـFoo<T>، حيث T هو معلمة نوع متباينة، الحد الأعلى (upper bound) هو TUpper، في الحالات التي يتم فيها قراءة القيم، Foo<*> يُشابه Foo<out TUpper>، وفي الحالات التي يتم فيها كتابة القيم، يُشابه Foo<in Nothing>.
إذا كان هناك عدة معلمات نوعية في نوع عام، فإن كل معلمة نوع يمكن أن تكون مُرسلة بشكل منفرد. على سبيل المثال، إذا تم تعريف النوع كـinterface Function<in T, out U>، فإنه يمكن أن يظهر ما يلي من أنواع نجمية:
Function<*, String>, يمثل Function<Nothing, String> ;
Function<Int, *>, يمثل Function<Int, Any?> ;
Function<, ، يمثل Function<Nothing, Any?> .
ملاحظة: رمز التشخيص مشابه جدًا للأنواع الأصلية في Java، ولكن يمكن استخدامه بشكل آمن