هذا المستند عبارة عن مجموعة قواعد لتأليف واجهات برمجة تطبيقات عامة في جافا وكوتلن بقصد أن تكون الشفرة مفهومة عند استخدامها من اللغة الأخرى.
جافا (للغة Kotlin)
لا كلمات رئيسية قوية
لا تستخدم أيًّا من الكلمات الرئيسية المشددة بلغة Kotlin كاسم للطرق أو الحقول. وتتطلب هذه الإجراءات استخدام علامات التجزئة للهروب عند الاتصال من Kotlin. يُسمح باستخدام الكلمات الرئيسية الخفيفة والكلمات الرئيسية المعدِّلة والمعرّفات الخاصة.
على سبيل المثال، تتطلّب الدالة when
في Mockito فواصل عليا عند استخدامها من لغة Kotlin:
val callable = Mockito.mock(Callable::class.java) Mockito.`when`(callable.call()).thenReturn(/* … */)
تجنب Any
إضافة
تجنَّب استخدام أسماء دوال الإضافات في Any
للطرق أو أسماء سمات الإضافة في Any
للحقول ما لم تكن هناك أسماء. ضروري للغاية. على الرغم من أن طرق وحقول الأعضاء ستكون لها دائمًا الأسبقية على وظائف إضافات Any
أو خصائصها، إلا أنه قد يكون من الصعب قراءة أي رمز يتم طلبه عند قراءة الرمز.
التعليقات التوضيحية للإبطال
يجب أن تتضمّن كل معلّمة بدائية وخاصية ونوع حقل في واجهة برمجة تطبيقات عامة تعليقًا توضيحيًا للقيم الفارغة. يتم تفسير الأنواع غير المزوَّدة بتعليقات توضيحية باعتبارها أنواع"وسيط عرض الإعلان"، والتي تتسم بقيم غامضة فارغة.
بشكل افتراضي، فإن علامات المحول البرمجي بلغة Kotlin تحترم التعليقات التوضيحية لـ JSR 305 ولكنها تضع علامة عليها تشير إلى وجود تحذيرات. يمكنك أيضًا تعيين علامة لجعل برنامج التجميع يتعامل مع التعليقات التوضيحية كأخطاء.
معلمات لامدا أخيرة
يجب أن تكون أنواع المعلمات المؤهّلة للإحالات الناجحة لـ SAM هي الأخيرة.
على سبيل المثال، يتم تعريف توقيع طريقة RxJava 2’s Flowable.create()
على النحو التالي:
public staticFlowable create( FlowableOnSubscribe source, BackpressureStrategy mode) { /* … */ }
نظرًا لأن FlowableOnSignup مؤهل لتحويل SAM، فإن استدعاءات الوظائف بهذه الطريقة من Kotlin تبدو كما يلي:
Flowable.create({ /* … */ }, BackpressureStrategy.LATEST)
ومع ذلك، إذا تم عكس المعلمات في توقيع الطريقة، فمن الممكن أن تستخدم استدعاءات الدالة بنية لاحقة lambda:
Flowable.create(BackpressureStrategy.LATEST) { /* … */ }
بادئات المواقع
يجب استخدام بادئة صارمة على غرار "الفاصولياء" لطريقة يتم تمثيلها كخاصية في لغة Kotlin.
تتطلّب طرق الموصّلات بادئة "get" أو بالنسبة إلى طرق الإرجاع المنطقية - يمكن استخدام البادئة "is".
public final class User { public String getName() { /* … */ } public boolean isActive() { /* … */ } }
val name = user.name // Invokes user.getName() val active = user.isActive // Invokes user.isActive()
تتطلب طرق التبديل المقترنة بادئة "set".
public final class User { public String getName() { /* … */ } public void setName(String name) { /* … */ } public boolean isActive() { /* … */ } public void setActive(boolean active) { /* … */ } }
user.name = "Bob" // Invokes user.setName(String) user.isActive = true // Invokes user.setActive(boolean)
إذا كنت تريد عرض الطرق كخصائص، لا تستخدم البادئات غير القياسية مثل "has"/"set" أو موصلات لا تبدأ بـ "get". تظل الطرق ذات البادئات غير القياسية قابلة للاستدعاء كدوال قد تكون مقبولة اعتمادًا على سلوك الطريقة.
التحميل الزائد من قِبل مشغل شبكة الجوال
يُرجى الانتباه إلى أسماء الطرق التي تسمح ببنية خاصة لمواقع الاتصال (مثل الحِمل الزائد على المشغِّل) في لغة Kotlin. تأكد من أن أسماء الطرق ذات معنى لاستخدامها مع البنية المختصرة.
public final class IntBox { private final int value; public IntBox(int value) { this.value = value; } public IntBox plus(IntBox other) { return new IntBox(value + other.value); } }
val one = IntBox(1) val two = IntBox(2) val three = one + two // Invokes one.plus(two)
Kotlin (لاستهلاك جافا)
اسم الملف
عندما يحتوي ملف على وظائف أو خصائص عالية المستوى، يمكنك التعليق عليه دائمًا باستخدام @file:JvmName("Foo")
لتوفير اسم لطيف.
سيتم تلقائيًا نقل أعضاء المستوى الأعلى في ملف MyClass.kt إلى صف باسم MyClassKt
لا يُعجبهم ذلك ويُسرّب اللغة في تفاصيل التنفيذ.
يمكنك إضافة @file:JvmMultifileClass
لدمج أعضاء المستوى الأعلى من ملفات متعددة في صف واحد.
وسيطات lambda
يجب أن تتجنّب أنواع الدوال
المُراد استخدامها من جافا نوع العرض
Unit
. ويتطلّب ذلك تحديد عبارة صريحة من return
Unit.INSTANCE;
غير متّسقة.
fun sayHi(callback: (String) -> Unit) = /* … */
// Kotlin caller: greeter.sayHi { Log.d("Greeting", "Hello, $it!") }
// Java caller: greeter.sayHi(name -> { Log.d("Greeting", "Hello, " + name + "!"); return Unit.INSTANCE; });
ولا تسمح هذه البنية أيضًا بتوفير نوع مُسمى دلاليًا بحيث يمكن تنفيذه على أنواع أخرى.
يؤدي تحديد واجهة طريقة مجردة ذات اسم واحد (SAM) باللغة Kotlin لنوع lambda إلى إصلاح المشكلة المتعلقة بلغة Java، ولكنه يمنع استخدام بنية lambda في Kotlin.
interface GreeterCallback { fun greetName(name: String): Unit } fun sayHi(callback: GreeterCallback) = /* … */
// Kotlin caller: greeter.sayHi(object : GreeterCallback { override fun greetName(name: String) { Log.d("Greeting", "Hello, $name!") } })
// Java caller: greeter.sayHi(name -> Log.d("Greeting", "Hello, " + name + "!"))
يؤدي تحديد واجهة SAM مُعنونة في جافا إلى استخدام إصدار أقل قليلاً من بنية Kotlin lambda حيث يجب تحديد نوع الواجهة بشكل صريح.
// Defined in Java: interface GreeterCallback { void greetName(String name); }
fun sayHi(greeter: GreeterCallback) = /* … */
// Kotlin caller: greeter.sayHi(GreeterCallback { Log.d("Greeting", "Hello, $it!") })
// Java caller: greeter.sayHi(name -> Log.d("Greeter", "Hello, " + name + "!"));
وفي الوقت الحالي، ليست هناك طريقة لتحديد نوع معلمة لاستخدامه كدالة lambda من كلٍّ من جافا وكوتلين بحيث تبدو اصطلاحية من كلتا اللغتين. الاقتراح الحالي هو تفضيل نوع الدالة على الرغم من انخفاض مستوى التجربة من جافا عندما يكون نوع العرض Unit
.
تجنّب أدوية Nothing
عامة
يتم عرض النوع الذي يكون المُعلَّمة العامة فيه Nothing
كأنواع أولية للغة جافا. نادرًا ما تُستخدم أنواع البيانات الأولية في جافا ويجب تجنبها.
استثناءات المستندات
يجب توثيق الدوال التي يمكنها عرض استثناءات محدّدة باستخدام @Throws
. يجب توثيق استثناءات وقت التشغيل في KDoc.
انتبِه إلى واجهات برمجة التطبيقات التي تفوض لها الدالة حيث إنها قد تطرح استثناءات تم تحديدها، وهو ما يسمح لغة Kotlin بنشره بدون تنبيه.
النُسخ الدفاعية
عند عرض مجموعات للقراءة فقط مملوكة أو مشتركة من واجهات برمجة تطبيقات عامة، يمكنك لفّها في حاوية غير قابلة للتعديل أو تنفيذ نسخة دفاعية. على الرغم من أن Kotlin يفرض ملكية خاصة بها للقراءة فقط، فليس هناك مثل هذا التنفيذ من جانب جافا. وبدون استخدام الغلاف أو النسخة الدفاعية، يمكن انتهاك الثوابت من خلال إرجاع مرجع مجموعة قديم.
الوظائف المصاحبة
يجب التعليق التوضيحي للدوال العامة في كائن مصاحب باستخدام
@JvmStatic
ليتم عرضها كطريقة ثابتة.
بدون التعليق التوضيحي، لا تتوفر هذه الدوال إلا كطرق افتراضية في حقل Companion
الثابت.
غير صحيح: بلا تعليق توضيحي
class KotlinClass { companion object { fun doWork() { /* … */ } } }
public final class JavaClass { public static void main(String... args) { KotlinClass.Companion.doWork(); } }
صحيح: @JvmStatic
تعليق توضيحي
class KotlinClass { companion object { @JvmStatic fun doWork() { /* … */ } } }
public final class JavaClass { public static void main(String... args) { KotlinClass.doWork(); } }
الثوابت المصاحبة
يجب إضافة تعليقات توضيحية إلى الخصائص العامة غير const
التي تكون الثوابت الفعّالة في companion object
باستخدام @JvmField
ليتم عرضها كحقل ثابت.
بدون التعليق التوضيحي، لا تتوفر هذه الخصائص إلا كأمثلة "مستلمين" لمثيل ذي اسم غريب في الحقل Companion
الثابت. يؤدي استخدام السمة @JvmStatic
بدلاً من السمة @JvmField
إلى نقل "المتغيّرين" ذوي الأسماء الغريبة إلى الطرق الثابتة في الصف، إلا أنّ ذلك لا يزال غير صحيح.
غير صحيح: بلا تعليق توضيحي
class KotlinClass { companion object { const val INTEGER_ONE = 1 val BIG_INTEGER_ONE = BigInteger.ONE } }
public final class JavaClass { public static void main(String... args) { System.out.println(KotlinClass.INTEGER_ONE); System.out.println(KotlinClass.Companion.getBIG_INTEGER_ONE()); } }
غير صحيح: @JvmStatic
تعليق توضيحي
class KotlinClass { companion object { const val INTEGER_ONE = 1 @JvmStatic val BIG_INTEGER_ONE = BigInteger.ONE } }
public final class JavaClass { public static void main(String... args) { System.out.println(KotlinClass.INTEGER_ONE); System.out.println(KotlinClass.getBIG_INTEGER_ONE()); } }
صحيح: @JvmField
تعليق توضيحي
class KotlinClass { companion object { const val INTEGER_ONE = 1 @JvmField val BIG_INTEGER_ONE = BigInteger.ONE } }
public final class JavaClass { public static void main(String... args) { System.out.println(KotlinClass.INTEGER_ONE); System.out.println(KotlinClass.BIG_INTEGER_ONE); } }
تسمية اصطلاحية
تختلف لغة استدعاء لغة Kotlin عن لغة Java، ما قد يؤدي إلى تغيير طريقة تسمية الدوال. استخدِم @JvmName
لتصميم أسماء بحيث تبدو اصطلاحية عند استخدام اصطلاحات اللغتَين أو مطابقة تسمية المكتبة العادية.
يحدث هذا غالبًا لوظائف الإضافات وخصائصها، نظرًا لاختلاف موقع نوع جهاز الاستقبال.
sealed class Optionaldata class Some (val value: T): Optional () object None : Optional () @JvmName("ofNullable") fun T?.asOptional() = if (this == null) None else Some(this)
// FROM KOTLIN: fun main(vararg args: String) { val nullableString: String? = "foo" val optionalString = nullableString.asOptional() }
// FROM JAVA: public static void main(String... args) { String nullableString = "Foo"; OptionaloptionalString = Optionals.ofNullable(nullableString); }
أحمال زائدة للدالات الافتراضية
يجب أن تستخدم الدوال التي تحتوي على قيم تلقائية قيمة @JvmOverloads
.
بدون هذا التعليق التوضيحي، من المستحيل استدعاء الدالة باستخدام أي قيم افتراضية.
عند استخدام @JvmOverloads
، افحص الطرق التي تم إنشاؤها للتأكد من أنها مفيدة. وإذا لم يحدث ذلك، فنفذ إحدى الخطوتين التاليتين أو كلتيهما حتى تصبح راضية:
- غيّر ترتيب المعلمات لتفضيل الإعدادات الافتراضية التي سيتم عرضها قريبًا في النهاية.
- انقل الإعدادات التلقائية إلى حِمل زائد للوظائف اليدوية.
غير صحيح: لا @JvmOverloads
class Greeting { fun sayHello(prefix: String = "Mr.", name: String) { println("Hello, $prefix $name") } }
public class JavaClass { public static void main(String... args) { Greeting greeting = new Greeting(); greeting.sayHello("Mr.", "Bob"); } }
صحيح: @JvmOverloads
تعليق توضيحي.
class Greeting { @JvmOverloads fun sayHello(prefix: String = "Mr.", name: String) { println("Hello, $prefix $name") } }
public class JavaClass { public static void main(String... args) { Greeting greeting = new Greeting(); greeting.sayHello("Bob"); } }
عمليات التحقق من Lint
المتطلبات
- إصدار "استوديو Android": 3.2 Canary 10 أو الإصدارات الأحدث
- إصدار المكون الإضافي Android Gradle: 3.2 أو الأحدث
الشيكات المعتمدة
تتوفّر حاليًا عمليات تحقّق من Android Lint لمساعدتك على رصد بعض مشاكل إمكانية التشغيل التفاعلي الموضّحة أعلاه والإبلاغ عنها. لا يتم حاليًا رصد سوى مشاكل جافا (لاستهلاك لغة Kotlin). على وجه التحديد، عمليات التحقق المتوافقة هي:
- اسم عارٍ غير معروف
- الدخول إلى الممتلكات
- لا توجد كلمات رئيسية بلغة Kotlin قوية
- معلمات لامدا أخيرة
استوديو Android
لتفعيل عمليات التحقّق هذه، انتقِل إلى ملف > الإعدادات المفضّلة > محرّر > عمليات الفحص وتحقّق من القواعد التي تريد تفعيلها ضمن إمكانية التشغيل التفاعلي Kotlin:
الشكل 1. إعدادات إمكانية التشغيل التفاعلي في Kotlin في "استوديو Android"
بعد التحقّق من القواعد التي تريد تفعيلها، سيتم تنفيذ عمليات التحقّق الجديدة عند تشغيل عمليات فحص الرمز (التحليل > فحص الرمز...)
إصدارات سطر الأوامر
لتفعيل عمليات التحقق هذه من إصدارات سطر الأوامر، أضِف السطر التالي في ملف build.gradle
:
رائع
android { ... lintOptions { enable 'Interoperability' } }
لغة Kotlin
android { ... lintOptions { enable("Interoperability") } }
للحصول على المجموعة الكاملة من التهيئات المتوافقة داخل lintOptions، راجع مرجع Gradle DSL لنظام التشغيل Android.
بعد ذلك، شغِّل ./gradlew lint
من سطر الأوامر.