مرحبا يا هبر! أوجه انتباهكم إلى ترجمة صفحة وثائق JetBrains Kotlin Coding Conventions.
→ الوثائق الأصلية
المحتوى:
باستخدام دليل الأسلوب في Intellij Idea
لتطبيق التنسيق في Intellij Idea وفقًا للدليل الحالي ، تحتاج إلى تثبيت الإصدار الإضافي لـ Kotlin 1.2.20 أو إصدار أحدث ، انتقل إلى الإعدادات | محرر | رمز النمط | Kotlin ، انقر على الرابط "Set from ..." في الركن الأيمن العلوي وحدد "دليل نمط محدد مسبقًا" / Kotlin style guide "من القائمة المنسدلة.
للتحقق من تنسيق الكود الخاص بك وفقًا للنمط الموصى به ، انتقل إلى إعداد الاستقصاء وقم بتمكين الاختيار "Kotlin | Style issues | لم يتم تنسيق الملف وفقًا لإعدادات المشروع". يتم تمكين قواعد التحقق من الصحة الأخرى ، مثل اصطلاحات التسمية ، افتراضيًا.
هيكل المشروع
هيكل المجلد
في المشروعات التي تستخدم لغات مختلفة ، يجب أن تكون الملفات التي تحتوي على كود Kotlin موجودة في نفس المجلد مثل الشفرة باللغات الأخرى وأن تستخدم نفس هيكل الملفات المقبول للغة الرئيسية. على سبيل المثال ، بالنسبة إلى Java ، يجب أن تكمن الملفات في بنية المجلد وفقًا لاسم الحزمة.
في المشروعات التي تستخدم Kotlin فقط ، تكون بنية المجلد الموصى بها هي: استخدام المجلدات لتنظيم الحزم مع تخطي الدليل الجذر ، أي إذا كانت جميع التعليمات البرمجية الموجودة في المشروع في الحزمة "org.example.kotlin" وحزمها ، فيجب أن تكون الملفات المصدر التي تنتمي إلى الحزمة "org.example.kotlin" في الدليل الجذر للمشروع ، والملفات التي تحتوي على الكود المصدري للحزمة "org. example.kotlin.foo.bar "يجب أن يكمن في الدليل الفرعي" foo / bar "نسبة إلى جذر المشروع.
اسم الملفات المصدر
إذا كان ملف Kotlin يحتوي على فئة واحدة فقط (ربما تتعلق بإعلان المستوى الأعلى) ، فيجب تسمية ذلك بالإضافة إلى فئة ذات ملحق .kt
. إذا كان الملف يحتوي على عدة فئات أو يحتوي فقط على إعلانات المستوى الأعلى ، فاختر اسمًا يصف ما يحتوي عليه الملف وقم بتسمية الملف وفقًا لذلك. استخدم سنام الجمل بالحرف الأول الكبير لتسمية الملفات (مثل ProcessDeclarations.kt
).
يجب أن يصف اسم الملف ما يفعله الكود الموجود في الملف. وهذا يعني أنه يجب عليك تجنب كلمات لا معنى لها مثل "Util" لتسمية الملفات.
تنظيم الملفات المصدر
يُعد وضع إعلانات متعددة (فئات أو وظائف أو خصائص المستوى الأعلى) في ملف Kotlin المصدر نفسه موضع ترحيب إذا كانت هذه التصريحات مرتبطة ارتباطًا وثيقًا ببعضها البعض ودلالة على أن حجم الملف لا يزال معقولًا (لا يزيد عن عدة مئات من الأسطر).
على وجه الخصوص ، عند تعريف وظائف الامتداد لفئة تنطبق على جميع جوانب تطبيق هذه الفئة ، ضعها في نفس الملف حيث تم تعريف الفئة نفسها. عند تعريف دالات الامتداد التي لها معنى فقط للسياق المحدد لاستخدام هذه الفئة ، ضعها بجانب الكود الذي يستخدم وظيفة الامتداد. لا تنشئ ملفات فقط لتخزين "جميع امتدادات Foo".
هيكل الطبقة
عادةً ما يتم فرز محتويات الفصل بالترتيب التالي:
- إقرارات الملكية وكتل التهيئة
- بناة الثانوية
- تصريحات الأسلوب
- كائنات رفيق
لا تقم بتصنيف تعريفات الطريقة أبجديًا أو بصريًا ، ولا تفصل الطرق العادية عن طرق الامتداد. بدلاً من ذلك ، ضع الكود المتصل منطقياً معًا حتى يتمكن الشخص الذي يقرأ الفصل من أعلى إلى أسفل من اتباع منطق ما يحدث. اختر ترتيبًا واحدًا (رمز المستوى الأعلى أولاً [الأشياء ذات المستوى الأعلى أولاً] والتفاصيل لاحقًا أو العكس) والتمسك بها.
ضع الفئات المتداخلة بجانب الرمز الذي يستخدم هذه الفئات. إذا كانت الفئات مخصصة للاستخدام الخارجي ولم يتم الرجوع إليها داخل الفصل ، فضعها في النهاية بعد الكائن المصاحب.
إطار تنفيذ الواجهة
عند تنفيذ واجهة ، حافظ على نفس بنية الواجهة التي يتم تنفيذها (إذا لزم الأمر ، وتناوبها مع الأساليب الخاصة الإضافية المستخدمة للتنفيذ)
يتجاوز الهيكل
إعادة التعريف وضعت دائما ، واحدا تلو الآخر.
تسمية القواعد
يتبع Kotlin نفس اصطلاحات التسمية مثل Java. على وجه الخصوص:
أسماء حزم صغيرة ولا تستخدم الشرطة السفلية (org.example.myproject). لا يوصى باستخدام أسماء الكلمات المتعددة عمومًا ، ولكن إذا كنت بحاجة إلى استخدام كلمات متعددة ، فيمكنك إما الجمع بينها أو استخدام الجمل الجمل (org.examle.myProject).
تبدأ الأسماء الكبيرة والكائنات بحرف كبير وتستخدم الجمل الحدباء:
open class DeclarationProcessor { ... } object EmptyDeclarationProcessor : DeclarationProcessor() { ... }
اسم الوظيفة
تبدأ أسماء الوظائف والخصائص والمتغيرات المحلية بحرف صغير ولا تحتوي على شرطات سفلية:
fun processDeclarations() { ... } var declarationCount = ...
استثناء: قد يكون لوظائف المصنع المستخدمة في إنشاء مثيل للفئات نفس اسم الفئة التي يتم إنشاؤها:
abstract class Foo { ... } class FooImpl : Foo { ... } fun Foo(): Foo { return FooImpl(...) }
اسم طرق الاختبار
في الاختبارات (وفقط في الاختبارات) يجوز استخدام أسماء الطرق ذات المسافات المحاطة بفواصل مقلوبة. (لاحظ أن أسماء الطرق هذه غير مدعومة حاليًا من قبل وقت تشغيل Android.) ويسمح أيضًا برموز النقاط السفلية في أسماء الطرق في رمز الاختبار.
class MyTestCase { @Test fun `ensure everything works`() { ... } @Test fun ensureEverythingWorks_onAndroid() { ... } }
تسمية الممتلكات
يجب أن تكون الأحرف الكبيرة (الخصائص المسمى const
، أو خصائص المستوى الأعلى أو كائن val
بدون وظيفة get
مخصصة تحتوي على بيانات غير قابلة للتغيير) كبيرة ، مفصولة بشرطة سفلية:
const val MAX_COUNT = 8 val USER_NAME_FIELD = "UserName"
يجب أن تستخدم أسماء المستوى الأعلى أو خصائص الكائن التي تحتوي على كائنات لها سلوك أو بيانات قابلة للتغيير أسماء شائعة في الحدبة:
val mutableCollection: MutableSet<String> = HashSet()
يمكن أن تستخدم أسماء الخصائص التي تشير إلى كائنات Singleton نفس نمط التسمية مثل تعريفات الفئة:
val PersonComparator: Comparator<Person> = ...
بالنسبة للتعدادات ، يمكنك استخدام الأسماء المكتوبة بأحرف كبيرة مفصولة بالشرطات السفلية أو بنمط الجمل الجمل ، بدءًا من الأحرف الكبيرة ، حسب الاستخدام.
enum class Color { RED, GREEN }
enum class Color { RedColor, GreenColor }
ملاحظة المترجم: لا تخلط بين أنماط مختلفة. اختيار نمط واحد والتمسك به في التصميم الخاص بك.
تسمية الخصائص الخفية
إذا كان للفصل صفتان متشابهتان من الناحية النظرية ، ولكن أحدهما جزء من واجهة برمجة التطبيقات العامة والآخر جزء من التنفيذ ، استخدم الشرطة السفلية كبادئة لاسم الخاصية المخفية:
class C { private val _elementList = mutableListOf<Element>() val elementList: List<Element> get() = _elementList }
اختيار الأسماء الصحيحة
عادة ما يكون اسم الفصل عبارة عن اسم أو عبارة تشرح ماهية الفصل الدراسي:
List, PersonReader
عادةً ما يكون اسم الأسلوب عبارة عن فعل أو عبارة توضح ما تفعله الطريقة:
close, readPersons
يجب أن يشير الاسم أيضًا إلى ما إذا كانت الطريقة تقوم بتغيير الكائن أو تقوم بإرجاع كائن جديد. على سبيل المثال ، يعد الفرز نوعًا يغير المجموعة ، والفرز هو إرجاع نسخة مصنفة جديدة من المجموعة.
يجب أن تشير الأسماء بوضوح إلى الغرض من الكيان ، لذلك من الأفضل تجنب استخدام كلمات لا معنى لها ( Manager
، Wrapper
، إلخ) في الأسماء.
عند استخدام الاختصار كجزء من اسم الإعلان ، استخدم الأحرف الكبيرة إذا كان يتكون من حرفين ( IOStream
) ؛ أو الأحرف الكبيرة فقط الحرف الأول ، إذا كان أطول ( XmlFormatter
، HttpInputStream
).
في معظم الحالات ، يتبع Kotlin اتفاقيات تنسيق Java.
استخدام 4 مسافات للمسافة البادئة. لا تستخدم علامات التبويب.
بالنسبة إلى الأقواس ، ضع دعامة الفتح في نهاية السطر الذي تبدأ به البنية ، ودعامة الإغلاق على خط منفصل ، محاذاة أفقياً مع بنية الفتح.
if (elements != null) { for (element in elements) {
(ملاحظة: في Kotlin ، فاصلة منقوطة اختيارية ، لذلك فإن التفاف الأسطر مهم. يفترض تصميم اللغة وجود أقواس متعرجة على نمط Java ، وقد تواجه سلوكًا غير متوقع في تنفيذ التعليمات البرمجية إذا حاولت استخدام نمط تنسيق مختلف.)
المساحات الأفقية
ضع مسافات حول العوامل الثنائية (a + b)
. استثناء: لا تضع مسافات حول عامل التشغيل "النطاق إلى" (0..i)
لا تضع مسافات حول المشغلين الأحاديين (a++)
ضع مسافات بين كلمات التحكم الرئيسية ( if
، when
، for
while
) وبين أقواس الفتح المقابلة.
لا تضع مسافة قبل قوس الفتح في الإعلان الأساسي للمنشئ أو الطريقة أو الطريقة.
class A(val x: Int) fun foo(x: Int) { ... } fun bar() { foo(1) }
لا تضع مسافة بعد (
، [
أو قبل ]
، )
.
لا تضع مساحة حول نقطة ما .
أو المشغل ?.
:
foo.bar().filter { it > 2 }.joinToString() foo?.()
ضع مسافة بعد الشرطة المائلة المزدوجة للتعليق //
:
لا تضع مسافات حول أقواس الزاوية المستخدمة للإشارة إلى معلمات الكتابة:
Class Map<K, V> { ... }
لا تضع مسافات حول النقطتين المزدوجة للإشارة إلى الأسلوب ::
class:
Foo::class String::length
لا تضع مساحة من قبل ?
تستخدم لتمييز null
:
String?
بشكل عام ، تجنب أي نوع من المحاذاة الأفقية. يجب ألا تؤثر إعادة تسمية معرف لاسم بطول مختلف على تنسيق الكود.
القولون
ضع مسافة قبل النقطتين :
في الحالات التالية:
- عندما يتم استخدامه لفصل نوع من نوع السوبر.
abstract class Foo<out T : Any>
- عند التفويض إلى مُنشئ فائق المستوى أو مُنشئ آخر من نفس الفئة ؛
constructor(x: String) : super(x) { ... } constructor(x: String) : this(x) { ... }
- بعد كائن الكلمة الأساسية.
val x = object : IFoo { ... }
لا تضع مساحة قبل :
عندما تفصل الإعلان ونوعه.
abstract fun foo(a: Int): T
دائما وضع الفضاء بعد:.
abstract class Foo<out T : Any> : IFoo { abstract fun foo(a: Int): T } class FooImpl : Foo() { constructor(x: String) : this(x) { ... } val x = object : IFoo { ... } }
يمكن كتابة الطبقات التي تحتوي على العديد من معلمات المنشئ الأساسية والأسماء القصيرة في سطر واحد:
class Person(id: Int, name: String)
يجب تنسيق الفئات التي تحتوي على أسماء أطول أو عدد المعلمات بحيث تكون كل معلمة رئيسية منشئ على سطر منفصل مع المسافة البادئة. أيضًا ، يجب أن تكون شريحة الإغلاق في سطر جديد. إذا استخدمنا الوراثة ، فيجب أن تكون الدعوة إلى مُنشئ الطبقة الفائقة أو قائمة الواجهات المنفذة موجودة على نفس الخط الموجود في الدعامة:
class Person( id: Int, name: String, surname: String ) : Human(id, name) { ... }
عند تحديد الواجهة واستدعاء مُنشئ الطبقة الفائقة ، يجب تحديد موقع مُنشئ الطبقة الفائقة أولاً ، ثم يكون اسم الواجهة على سطر جديد له ما يبرره:
class Person( id: Int, name: String, surname: String ) : Human(id, name), KotlinMaker { ... }
بالنسبة للفئات التي تحتوي على قائمة طويلة من الأنواع الفائقة ، تحتاج إلى وضع فاصل أسطر بعد النقطتين ومحاذاة جميع أسماء الأنواع الفائقة أفقياً إلى اليسار:
class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } }
للفصل بوضوح بين عنوان الفصل ونصه عندما يكون عنوان الفصل طويلًا ، إما وضع سطر فارغ بعد عنوان الفصل (كما في المثال أعلاه) ، أو ضع قوس الفتح على سطر منفصل:
class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } }
استخدم المسافة البادئة العادية (4 مسافات) لمعلمات المنشئ.
الأساس المنطقي: هذا يضمن أن الخصائص المعلنة في المُنشئ الرئيسي لها نفس المسافة البادئة مثل الخصائص المُعلنة في نص الفصل.
المعدلات
إذا كان الإعلان يحتوي على عدة معدّلات ، فاعمل دائمًا على ترتيبها بالترتيب التالي:
public / protected / private / internal expect / actual final / open / abstract / sealed / const external override lateinit tailrec vararg suspend inner enum / annotation companion inline infix operator data
ضع جميع التعليقات التوضيحية قبل المعدلات:
@Named("Foo") private val foo: Foo
إذا كنت لا تعمل في مكتبة ، فاحذف المعدلات الزائدة (مثل العامة).
عادةً ما يتم وضع التعليقات التوضيحية في سطور منفصلة قبل الإعلان المرفق بها ، وبنفس المسافة البادئة:
@Target(AnnotationTarget.PROPERTY) annotation class JsonExclude
يمكن العثور على التعليقات التوضيحية بدون وسيطات في سطر واحد:
@JsonExclude @JvmField var x: String
يمكن وضع تعليق توضيحي واحد بدون وسائط على نفس سطر الإعلان المقابل:
@Test fun foo() { ... }
ملف التعليقات التوضيحية
يتم وضع التعليقات التوضيحية على الملفات بعد التعليق على الملف (إن وجد) ، قبل بيان الحزمة ويتم فصلها عن الحزمة بواسطة سطر فارغ (للتأكيد على حقيقة أنها موجهة إلى الملف وليس إلى الحزمة).
@file:JvmName("FooBar") package foo.bar
إذا لم يكن احتواء توقيع الأسلوب على سطر واحد ، استخدم بناء الجملة التالي:
fun longMethodName( argument: ArgumentType = defaultValue, argument2: AnotherArgumentType ): ReturnType {
استخدم المسافة البادئة العادية (4 مسافات) لمعلمات الوظيفة.
الأساس المنطقي: الاتساق مع المعلمات منشئ
من الأفضل استخدام تعبير بدون أقواس متعرجة للوظائف التي تتكون من سطر واحد.
fun foo(): Int {
إذا كان نص دالة سطر مفرد لا يتناسب مع نفس سطر الإعلان ، فضع علامة = في السطر الأول. وضع مسافة بادئة لجسم التعبير بمقدار 4 مسافات.
fun f(x: String) = x.length
بالنسبة إلى الخصائص البسيطة للقراءة فقط ، من الأفضل استخدام التنسيق أحادي الأسطر:
val isEmpty: Boolean get() = size == 0
للحصول على خصائص أكثر تعقيدًا ، استخدم دائمًا get
على set
في خطوط منفصلة:
val foo: String get() { ... }
بالنسبة للخصائص ذات التهيئة ، إذا كانت أداة التهيئة طويلة جدًا ، أضف فاصل أسطر بعد العلامة المتساوية والمسافة البادئة لأربعة مسافات لسلسلة التهيئة:
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
إذا كانت الحالة في if
أو if
بيان التحكم متعدد الأسطر ، فاستخدم دائمًا دعامات مجعد حول نص البيان. ضع مسافة بادئة لكل سطر لاحق من الشرط بـ 4 مسافات بالنسبة لبداية العبارة. ضع أقواس الإغلاق للحالة مع دعامة مجعد الفتح على سطر منفصل:
if (!component.isSyncing && !hasAnyKotlinRuntimeInScope(module) ) { return createKotlinNotConfiguredPanel(module) }
التبرير: محاذاة أنيق وفصل واضح من الجسم الشرط والجسم الشرط
ضع المفتاح else
، catch
، finally
الكلمات الأساسية ، وكذلك الكلمة المفتاحية الخاصة بـ do / أثناء حلقة على نفس السطر مثل قوس مجعد الإغلاق السابق:
if (condition) {
إذا كانت شروط التعليمات تتكون من عدة كتل ، فمن المستحسن فصلها عن بعضها البعض بخط فارغ:
private fun parsePropertyValue(propName: String, token: Token) { when (token) { is Token.ValueToken -> callback.visitValue(propName, token.value) Token.LBRACE -> {
ضع الكتل القصيرة when
العبارات على نفس السطر بدون أقواس مجعد.
when (foo) { true -> bar()
عند استخدام قائمة طويلة من المعلمات ، ضع فاصل الأسطر بعد الأقواس. المسافة البادئة 4 مسافات وتجميع الوسائط المرتبطة منطقياً في سطر واحد.
drawSquare( x = 10, y = 10, width = 100, height = 100, fill = true )
استخدم مسافات حول علامة المساواة بين اسم المعلمة وقيمته.
عند استخدام المكالمات بالسلاسل ، ضع .
او ?.
عوامل التشغيل على سطر جديد مع مسافة بادئة واحدة في 4 مسافات:
val anchor = owner ?.firstChild!! .siblings(forward = true) .dropWhile { it is PsiComment || it is PsiWhiteSpace }
يجب أن تحتوي المكالمة الأولى في السلسلة عادةً على فاصل أسطر أمامها ، لكن من الطبيعي ألا تجعلها في حالة قراءة الكود بشكل أفضل ومن المنطقي.
في تعبيرات لامدا ، يجب استخدام المسافات حول الأقواس المتعرجة وحول السهم الذي يفصل المعلمات عن الجسم. إذا قبلت المكالمة شخصية lambda واحدة ، فينبغي استخدامها خارج الأقواس كلما كان ذلك ممكنًا.
list.filter { it > 10 }
عند تعيين تسمية إلى تعبير lambda ، لا تضع مسافة بين الملصق ومفتاح الفتح:
fun foo() { ints.forEach lit@{
عند إعلان أسماء المعلمات في تعبير lambda متعدد الأسطر ، ضع الأسماء في السطر الأول ، ثم السهم ، وعلى السطر الجديد ، بداية نص الدالة:
appendCommaSeparated(properties) { prop -> val propertyValue = prop.get(obj)
إذا لم تكن قائمة المعلمات مناسبة لسطر واحد ، فضع السهم في سطر منفصل:
foo { context: Context, environment: Env -> context.configureEnv(environment) }
عند استخدام الوثائق متعددة الخطوط ، ضع /**
على سطر منفصل ، وابدأ كل سطر لاحق بعلامة النجمة:
يمكن وضع وثائق قصيرة على سطر واحد:
بشكل عام ، تجنب استخدام علامات param والعودة . بدلاً من ذلك ، قم بتضمين وصف للمعلمات وقيم الإرجاع مباشرةً في تعليق الوثائق وإضافة مراجع المعلمات أينما تم ذكرها. استخدم المعلمة والعودة فقط عندما يكون هناك حاجة إلى وصف طويل لا يتناسب مع معنى النص الرئيسي.
تجنب الإنشاءات غير الضرورية
تعتبر العديد من التركيبات النحوية في Kotlin اختيارية ويتم تمييزها بواسطة بيئة التطوير باعتبارها غير ضرورية ؛ يجب عدم استخدامها في الكود الخاص بك فقط لجعل الكود "واضحًا".
استخدام وحدة الكلمات الرئيسية
في الوظائف ، لا ينبغي استخدام كلمة الوحدة الرئيسية:
fun foo() {
فاصلة منقوطة
تجنب استخدام فاصلة منقوطة في كل فرصة.
أنماط السلسلة
لا تستخدم الأقواس المتعرجة عند إدخال متغير بسيط في سلسلة قالب. استخدم الأقواس المتعرجة فقط للتعبيرات الطويلة.
println("$name has ${children.size} children")
اصطلاح استخدام ميزات اللغة
ثبات
يفضل استخدام البيانات غير القابلة للتغيير قبل البيانات القابلة للتغيير. قم دائمًا بتعريف المتغيرات والخصائص المحلية على أنها val
، وليس var
، إلا إذا كانت تتغير بالفعل.
استخدم دائمًا واجهات مجموعة غير قابلة للتغيير ( Collection
، List
، Set
، Map
) لإعلان المجموعات التي لا تتغير. في كل فرصة ، عند استخدام أساليب المصنع لإنشاء مجموعة ، استخدم تطبيقًا يُرجع المجموعات الثابتة:
: .
.
[Type alias]
, , :
typealias MouseClickHandler = (Any, MouseEvent) -> Unit typealias PersonIndex = Map<String, Person>
-
-, , it
. - .
-
. - , . , - .
( @
) .
, , boolean
, .
drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)
try
, if
when
, return
:
return if (x) foo() else bar()
if
when
if
when
when (x) { null -> ... else -> ... } if (x == null) ... else ...
, when
.
Boolean?
Boolean?
, if (value == true)
if (value == false)
, if (value ?: false)
if (value != null && value)
.
filtet
, map
.. . : forEach
( for
null forEach
)
, , , .
until
( ):
for (i in 0..n - 1) { ... }
.
\n
escape- .
, trimIndent
, , trimMargin
, :
assertEquals( """ Foo Bar """.trimIndent(), value ) val a = """if(a > 1) { | return a |}""".trimMargin()
. , , .
:
. , , , , . API, , . , .
infix
, , . : and
, to
, zip
. : add
.
infix
, .
, , . , , . , , .
class Point(val x: Double, val y: Double) { companion object { fun fromPolar(angle: Double, radius: Double) = Point(...) } }
, , .
: , , Kotlin null
, null
public
/, , Kotlin:
fun apiCall(): String = MyJavaApi.getProperty("name")
(package-level class-level) Kotlin:
class Person { val name: String = MyJavaApi.getProperty("name") }
, , Kotlin :
fun main() { val name = MyJavaApi.getProperty("name") println(name) }
apply
/ with
/ run
/ also
/ let
Kotlin . , :
- ? , ,
it
, this
( also
let
). also
, .
- ? ,
apply
also
. , with
, let
run
.
- null ? ,
apply
, let
run
. , with
also
.
API:
- ( API)
- ( )
- KDoc public api, , /