این سند بهعنوان تعریف کامل استانداردهای کدنویسی Android برای کد منبع در «زبان برنامهنویسی Kotlin» عمل میکند. فایل منبع Kotlin تنها درصورتی بهعنوان «سبک Google Android» توصیف میشود که از قوانین مندرج در اینجا پیروی کند.
مانند سایر راهنمای سبک برنامهنویسی، مسائل پوششدادهشده نه تنها مسائل زیباییشناختی قالببندی، بلکه انواع دیگر قراردادها یا استانداردهای کدنویسی را نیز دربرمیگیرد. بااینحال، این سند عمدتاً بر قوانین سختگیرانهای که بهطور جهانی از آنها پیروی میکنیم تمرکز دارد و از ارائه توصیههایی که بهوضوح قابلاجرا نیستند (چه توسط انسان و چه توسط ابزار) اجتناب میکند.
فایلهای منبع
همه فایلهای منبع باید بهعنوان UTF-8 کدبندی شوند.
نامگذاری
اگر فایل منبع فقط حاوی یک کلاس سطح بالا باشد، نام فایل باید نام حساس به حروف کوچک و بزرگ بهعلاوه پسوند .kt را منعکس کند. درغیراینصورت،
اگر فایل منبع حاوی چندین بیانیه سطح بالا است، نامی را انتخاب کنید
که محتوای فایل را توصیف کند، از PascalCase استفاده کنید (اگر نام فایل جمع باشد،
استفاده از camelCase قابلقبول است)، و پسوند .kt را اضافه کنید.
// MyClass.kt class MyClass { }
// Bar.kt class Bar { } fun Runnable.toBar(): Bar = Bar()
// Map.kt fun <T, O> Set<T>.map(func: (T) -> O): List<O> = emptyList() fun <T, O> List<T>.map(func: (T) -> O): List<O> = emptyList()
// extensions.kt fun MyClass.process() = { /* ... */ } fun MyResult.print() = { /* ... */ }
نویسههای ویژه
نویسههای فاصله سفید
بهجز توالی پایاندهنده خط، نویسه فضای افقی ASCII (0x20) تنها نویسه فضای سفیدی است که در هر جایی از فایل منبع ظاهر میشود. این یعنی:
- همه نویسههای فاصله سفید دیگر در رشته و حرفثابتهای نویسه از آنها گریز میشود.
- از نویسههای جدولی برای تورفتگی استفاده نمیشود.
توالیهای گریز ویژه
برای هر نویسهای که توالی گریز ویژه دارد
(\b، \n، \r، \t، \'، \"، \\، و \$)،
از آن توالی بهجای نویسه یونیکد مربوطه
(مثلاً \u000a) استفاده میشود.
نویسههای غیر ASCII
برای نویسههای غیر ASCII باقیمانده، یا از نویسه واقعی «یونیکد» (برای نمونه، ∞) یا از گریز «یونیکد» معادل (برای نمونه، \u221e) استفاده میشود.
انتخاب فقط به این بستگی دارد که کدامیک کد را
آسانتر برای خواندن و درک کردن میکند.
استفاده از نویسههای فرار یونیکد برای نویسههای چاپشدنی در هر مکانی توصیه نمیشود و
استفاده از آنها در خارج از رشتههای حرفی و نظرات بهشدت توصیه نمیشود.
| مثال | بحث |
|---|---|
val unitAbbrev = "μs" |
بهترین: کاملاً واضح حتی بدون نظر. |
val unitAbbrev = "\u03bcs" // μs |
ضعیف: هیچ دلیلی برای استفاده از نویسه گریز با نویسه چاپشدنی وجود ندارد. |
val unitAbbrev = "\u03bcs" |
ضعیف: خواننده نمیداند این چیست. |
return "\ufeff" + content |
خوب: برای نویسههای غیرقابل چاپ از نویسههای گریز استفاده کنید و درصورت لزوم نظر دهید. |
ساختار
فایل .kt شامل موارد زیر است، به ترتیب:
- سرایند حق نشر و/یا پروانه (اختیاری)
- حاشیهنویسیهای سطح فایل
- بیانیه بسته
- وارد کردن صورتوضعیتها
- اظهارنامههای سطح بالا
دقیقاً یک خط خالی هریک از این بخشها را از هم جدا میکند.
حق نشر / پروانه
اگر سرصفحه حق نشر یا پروانه به فایل تعلق دارد، باید در بالای آن در یک نظر چندخطی قرار گیرد.
/* * Copyright 2017 Google, Inc. * * ... */
از KDoc-style یا نظر تکخطی استفاده نکنید.
/** * Copyright 2017 Google, Inc. * * ... */
// Copyright 2017 Google, Inc. // // ...
حاشیهنویسیهای سطح فایل
حاشیهنویسیهای دارای استفاده از هدف سایت «فایل» بین هر نظر سرصفحه و بیانیه بسته قرار میگیرند.
بیانیه بسته
بیانیه بسته مشمول هیچ محدودیت ستونی نیست و هرگز در خطوط شکسته نمیشود.
وارد کردن صورتوضعیتها
عبارتهای وارد کردن برای کلاسها، توابع، و داراییها در یک فهرست واحد گروهبندی و براساس ASCII مرتب میشوند.
وارد کردن با کارت عام (از هر نوعی) مجاز نیست.
مشابه با بیانیه بسته، بیانیههای وارد کردن مشمول محدودیت ستون نیستند و هرگز خطپیچ نمیشوند.
اظهارنامههای سطح بالا
فایل .kt میتواند یک یا چند نوع، تابع، دارایی، یا
نام مستعار نوع را در سطح بالا اعلام کند.
محتوای فایل باید بر یک موضوع واحد متمرکز باشد. مثالهای این مورد میتواند یک نوع عمومی واحد یا مجموعهای از توابع افزونه باشد که عملکرد یکسانی را روی چندین نوع گیرنده انجام میدهند. اظهارات غیرمرتبط باید در فایلهای جداگانه قرار گیرند و اظهارات عمومی در یک فایل باید به حداقل برسند.
هیچ محدودیت صریحی برای تعداد یا ترتیب محتوای فایل وجود ندارد.
فایلهای منبع معمولاً از بالا به پایین خوانده میشوند، به این معنی که ترتیب، بهطورکلی، باید نشان دهد که اظهارات بالاتر به درک اظهارات پایینتر کمک میکنند. فایلهای مختلف ممکن است محتوایشان را به روشهای مختلفی مرتب کنند. بههمین ترتیب، یک فایل ممکن است حاوی ۱۰۰ دارایی، فایل دیگر حاوی ۱۰ تابع، و فایل دیگر حاوی یک کلاس باشد.
مهم این است که هر فایل از ترتیب منطقی خاصی استفاده کند که نگهدارنده آن درصورت درخواست بتواند آن را توضیح دهد. برای مثال، توابع جدید صرفاً بهصورت عادتوار به انتهای فایل اضافه نمیشوند، زیرا این کار منجر به ترتیب «زمانی براساس تاریخ اضافه شدن» میشود که ترتیب منطقی نیست.
ترتیب اعضای کلاس
ترتیب اعضا در یک کلاس از همان قوانین بیانیههای سطح بالا پیروی میکند.
قالببندی
براکت
برای شاخههای when و عبارتهای if که بیشاز یک شاخه else ندارند و در یک خط جا میشوند، نیازی به آکولاد نیست.
if (string.isEmpty()) return val result = if (string.isEmpty()) DEFAULT_VALUE else string when (value) { 0 -> return // … }
درغیراینصورت، برای هر شاخه if، for، when، do،
و عبارت و گزاره while، حتی زمانی که بدنه خالی باشد یا فقط حاوی
یک گزاره باشد، به آکولاد نیاز است.
if (string.isEmpty()) return // WRONG! if (string.isEmpty()) { return // Okay } if (string.isEmpty()) return // WRONG else doLotsOfProcessingOn(string, otherParametersHere) if (string.isEmpty()) { return // Okay } else { doLotsOfProcessingOn(string, otherParametersHere) }
بلوکهای غیرخالی
آکولادها از سبک Kernighan و Ritchie («قلابهای مصری») برای بلوکهای غیرخالی و ساختارهای شبیه بلوک پیروی میکنند:
- قبلاز آکولاد باز، شکست خط وجود ندارد.
- شکستن خط بعداز آکولاد باز.
- شکستن خط قبلاز آکولاد بسته.
- پساز آکولاد بسته، فقط اگر آن آکولاد یک عبارت یا بدنه تابع، سازنده، یا کلاس نامگذاریشده را خاتمه دهد، شکست خط ایجاد میشود.
برای مثال، اگر آکولاد با
elseیا کاما دنبال شود، پساز آن شکست خط وجود ندارد.
return Runnable { while (condition()) { foo() } }
return object : MyClass() { override fun foo() { if (condition()) { try { something() } catch (e: ProblemException) { recover() } } else if (otherCondition()) { somethingElse() } else { lastThing() } } }
چند استثنا برای کلاسهای شمارشی در زیر آورده شده است.
بلوکهای خالی
بلوک خالی یا ساختار شبهبلوک باید به سبک K&R باشد.
try { doSomething() } catch (e: Exception) {} // WRONG!
try { doSomething() } catch (e: Exception) { } // Okay
عبارات
یک شرط if/else که بهعنوان عبارت استفاده میشود ممکن است
آکولادها را فقط درصورتی حذف کند که کل عبارت در یک خط جا شود.
val value = if (string.isEmpty()) 0 else 1 // Okay
val value = if (string.isEmpty()) // WRONG! 0 else 1
val value = if (string.isEmpty()) { // Okay 0 } else { 1 }
تورفتگی
هر بار که ساختار جدیدی شبیه بلوک یا بلوک باز میشود، تورفتگی چهار فاصله افزایش مییابد. وقتی بلوک تمام میشود، تورفتگی به سطح تورفتگی قبلی برمیگردد. سطح تورفتگی برای کد و نظرات در سراسر بلوک اعمال میشود.
یک بیانیه در هر خط
هر عبارت با یک شکست خط دنبال میشود. از نقطهویرگول استفاده نمیشود.
پیچیدن خط
کد دارای محدودیت ستونی ۱۰۰ نویسه است. بهجز مواردی که در زیر ذکر شده است، هر خطی که از این حد فراتر رود باید بهصورت خطپیچ باشد، همانطور که در زیر توضیح داده شده است.
استثنائات:
- خطهایی که رعایت محدودیت ستون در آنها ممکن نیست (برای نمونه، نشانی وب طولانی در KDoc)
- بیانیههای
packageوimport - خطوط فرمان در یک نظر که ممکن است در یک پوسته برش و چسبانده شود
کجا شکسته شود
دستورالعمل اصلی برای شکستن خط این است: ترجیحاً در سطح نحوی بالاتر شکسته شود. همچنین،
- وقتی خطی در نام تابع میانوند یا عملگر شکسته میشود، شکستگی بعداز نام تابع میانوند یا عملگر رخ میدهد.
- وقتی خط در نمادهای «اپراتورمانند» زیر شکسته میشود، شکستگی
قبلاز نماد رخ میدهد:
- جداکننده نقطه (
.،?.). - دو نقطه عضو مرجع (
::).
- جداکننده نقطه (
- نام روش یا سازنده به پرانتز باز (
() که بعداز آن میآید متصل میماند. - ویرگول (
,) به نشانه قبلاز خود متصل میماند. - پیکان لامبدا (
->) به فهرست آرگومانهایی که قبلاز آن میآید متصل میماند.
توابع
وقتی امضای تابع در یک خط جا نمیشود، هر بیانیه پارامتر را در خط جداگانهای قرار دهید. پارامترهای تعریفشده در این قالب باید از یک تورفتگی (+۴) استفاده کنند. پرانتز بسته ()) و نوع برگشتی در خط جداگانهای بدون تورفتگی اضافی قرار میگیرند.
fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "" ): String { // ... }
توابع عبارت
وقتی تابعی فقط یک عبارت دارد، میتواند بهصورت تابع عبارت نشان داده شود.
override fun toString(): String { return "Hey" }
override fun toString(): String = "Hey"
مشخصات
وقتی مقداردهنده اولیه دارایی در یک خط جا نمیشود، پساز علامت مساوی (=) خط را بشکنید و از تورفتگی استفاده کنید.
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
داراییهایی که تابع get و/یا set را اعلام میکنند باید هرکدام را در
خط خودشان با تورفتگی معمولی (+۴) قرار دهند. آنها را بااستفاده از همان قوانین
توابع قالببندی کنید.
var directory: File? = null set(value) { // … }
val defaultExtension: String get() = "kt"
فاصله سفید
عمودی
یک خط خالی ظاهر میشود:
- بین اعضای متوالی یک کلاس: ویژگیها، سازندهها،
توابع، کلاسهای تودرتو، و غیره.
- استثنا: خط خالی بین دو دارایی متوالی (بدون کد دیگری بین آنها) اختیاری است. از چنین خطوط خالی درصورت نیاز برای ایجاد گروهبندیهای منطقی داراییها و مرتبط کردن داراییها با دارایی پشتیبان آنها، درصورت وجود، استفاده میشود.
- استثنا: خطوط خالی بین ثابتهای شمارشی در زیر پوشش داده شده است.
- بین دستورات، درصورت نیاز برای سازماندهی کد به زیربخشهای منطقی.
- اختیاری قبلاز اولین عبارت در یک تابع، قبلاز اولین عضو یک کلاس، یا بعداز آخرین عضو یک کلاس (نه تشویق میشود و نه منع میشود).
- همانطور که در بخشهای دیگر این سند (مثل بخش ساختار) الزامی است.
چند خط خالی متوالی مجاز است، اما توصیه نمیشود یا هرگز لازم نیست.
افقی
علاوهبر مواردی که زبان یا قوانین سبک دیگر الزامی کرده است، و بهجز حرفیها، نظرات، و KDoc، یک فاصله ASCII فقط در مکانهای زیر نیز ظاهر میشود:
- جدا کردن هر کلمه رزروشده، مانند
if،for، یاcatchاز پرانتز باز (() که در همان خط بعداز آن میآید.// WRONG! for(i in 0..1) { }
// Okay for (i in 0..1) { }
- جدا کردن هر کلمه رزروشده، مانند
elseیاcatch، از آکلاد بسته (}) که در آن خط قبلاز آن قرار دارد.// WRONG! }else { }
// Okay } else { }
-
قبلاز هر آکولاد باز (
{).// WRONG! if (list.isEmpty()){ }
// Okay if (list.isEmpty()) { }
-
در هر دو طرف هر عملگر دوتایی.
// WRONG! val two = 1+1
این امر برای نمادهای «شبیه اپراتور» زیر نیز اعمال میشود:// Okay val two = 1 + 1
- پیکان در عبارت لامبدا (
->).// WRONG! ints.map { value->value.toString() }
// Okay ints.map { value -> value.toString() }
-
دو نقطه (
::) در ارجاع عضو.// WRONG! val toString = Any :: toString
// Okay val toString = Any::toString
-
جداکننده نقطه (
.).// WRONG it . toString()
// Okay it.toString()
-
عملگر محدوده (
..).// WRONG for (i in 1 .. 4) { print(i) }
// Okay for (i in 1..4) { print(i) }
- پیکان در عبارت لامبدا (
-
فقط قبلاز دونقطه (
:) اگر در بیانیه کلاس برای مشخص کردن کلاس پایه یا میانهها استفاده شود، یا وقتی در بندwhereبرای محدودیتهای عمومی استفاده شود.// WRONG! class Foo: Runnable
// Okay class Foo : Runnable
// WRONG fun <T: Comparable> max(a: T, b: T)
// Okay fun <T : Comparable<T>> max(a: T, b: T)
// WRONG fun <T> max(a: T, b: T) where T: Comparable<T>
// Okay fun <T> max(a: T, b: T) where T : Comparable<T> {}
-
پساز ویرگول (
,) یا دونقطه (:).// WRONG! val oneAndTwo = listOf(1,2)
// Okay val oneAndTwo = listOf(1, 2)
// WRONG! class Foo :Runnable
// Okay class Foo : Runnable
-
در هر دو طرف خط مورب دوتایی (
//) که نظر پایان خط را شروع میکند. در اینجا، چندین فضا مجاز است، اما الزامی نیست.// WRONG! var debugging = false//disabled by default
// Okay var debugging = false // disabled by default
این قانون هرگز بهعنوان الزام یا منع فضای اضافی در ابتدا یا انتهای خط تفسیر نمیشود؛ فقط به فضای داخلی میپردازد.
ساختارهای خاص
کلاسهای شمارشی
تعداد ثابتهای شمارشی بدون تابع و بدون مستندات ممکن است بهصورت اختیاری بهعنوان یک خط قالببندی شود.
enum class Answer { YES, NO, MAYBE }
وقتی ثابتهای یک شمارش در خطوط جداگانه قرار میگیرند، بین آنها به خط خالی نیاز نیست، بهجز در مواردی که بدنه را تعریف میکنند.
enum class Answer { YES, NO, MAYBE { override fun toString() = """¯\_(ツ)_/¯""" } }
ازآنجاییکه کلاسهای شمارشی کلاس هستند، همه قوانین دیگر برای قالببندی کلاسها اعمال میشود.
حاشیهنویسیها
حاشیهنویسیهای عضو یا نوع در خطوط جداگانه بلافاصله قبلاز ساختار حاشیهنویسیشده قرار میگیرند.
@Retention(SOURCE) @Target(FUNCTION, PROPERTY_SETTER, FIELD) annotation class Global
حاشیهنویسیهای بدون آرگومان را میتوان در یک خط قرار داد.
@JvmField @Volatile var disposable: Disposable? = null
وقتی فقط یک شرح بدون آرگومان وجود داشته باشد، ممکن است در همان خط بیانیه قرار گیرد.
@Volatile var disposable: Disposable? = null @Test fun selectAll() { // … }
نحو @[...] فقط میتواند با هدف استفاده-سایت صریح استفاده شود، و فقط برای
ترکیب کردن ۲ یا چند گزارمان بدون آرگومان در یک خط.
@field:[JvmStatic Volatile] var disposable: Disposable? = null
انواع دارایی/برگشتی ضمنی
اگر بدنه تابع عبارت یا مقداردهی اولیه دارایی یک مقدار عددی باشد یا نوع برگشتی را بتوان بهوضوح از بدنه استنباط کرد، دراینصورت میتوان آن را حذف کرد.
override fun toString(): String = "Hey" // becomes override fun toString() = "Hey"
private val ICON: Icon = IconLoader.getIcon("/icons/kotlin.png") // becomes private val ICON = IconLoader.getIcon("/icons/kotlin.png")
هنگام نوشتن کتابخانه، وقتی که بخشی از API عمومی است، اعلان نوع صریح را حفظ کنید.
نامگذاری
شناسهها فقط از حروف و ارقام ASCII و در تعداد کمی از موارد که در زیر ذکر شده است، از زیرخط استفاده میکنند. بنابراین، هر نام شناسه معتبر با عبارت باقاعده \w+ مطابقت داده میشود.
پیشوندها یا پسوندهای ویژه، مانند آنچه در مثالها دیده میشود
name_، mName، s_name، و kName، بهجز در مورد
داراییهای پشتیبان (به
داراییهای پشتیبان مراجعه کنید) استفاده نمیشوند.
نام بستهها
نامهای بسته کاملاً با حروف کوچک است و کلمات متوالی بهسادگی بههم متصل میشوند (بدون زیرخط).
// Okay package com.example.deepspace // WRONG! package com.example.deepSpace // WRONG! package com.example.deep_space
نامها را تایپ کنید
نامهای کلاس به زبان PascalCase نوشته میشوند و معمولاً اسم یا عبارت اسمی هستند. برای مثال، Character یا ImmutableList. نامهای میانای کاربری ممکن است
اسم یا عبارت اسمی (برای مثال، List) باشند، اما
گاهی اوقات ممکن است صفت یا عبارت صفتی
باشند (برای مثال، Readable).
نام کلاسهای آزمایش با نام کلاسی که آزمایش میکنند شروع میشود،
و با Test پایان مییابد. برای مثال، HashTest یا
HashIntegrationTest.
نامهای تابع
نامهای تابع به زبان camelCase نوشته میشوند و معمولاً فعل یا عبارت فعلی هستند. برای مثال، sendMessage یا stop.
زیرخطها مجاز هستند در نامهای تابع آزمایشی ظاهر شوند تا اجزای منطقی نام را از هم جدا کنند.
@Test fun pop_emptyStack() { // … }
توابع حاشیهنویسیشده با @Composable که Unit برمیگردانند بهصورت PascalCased هستند و بهعنوان اسم نامگذاری میشوند، انگار که نوع هستند.
@Composable fun NameTag(name: String) { // … }
نام تابع نباید فاصله داشته باشد زیرا در همه پلاتفرمها پشتیبانی نمیشود (بهویژه در Android بهطور کامل پشتیبانی نمیشود).
// WRONG! fun `test every possible case`() {} // OK fun testEveryPossibleCase() {}
نامهای ثابت
نامهای ثابت از UPPER_SNAKE_CASE استفاده میکنند: همه حروف بزرگ، با کلماتی که با زیرخط از هم جدا میشوند. اما ثابت دقیقاً چیست؟
ثابتها val دارایی بدون تابع سفارشی get هستند که محتوای آنها
بهشدت تغییرناپذیر است و توابع آنها هیچ اثر جانبی قابلتشخیصی ندارند. این شامل انواع تغییرناپذیر و مجموعههای تغییرناپذیر از انواع تغییرناپذیر و همچنین مقادیر عددی و رشتهای درصورت علامتگذاری بهعنوان const میشود. اگر هریک از وضعیتهای قابلمشاهده نمونه بتواند تغییر کند، ثابت نیست. صرفاً قصد داشتن برای
هرگز جهش ندادن شیء کافی نیست.
const val NUMBER = 5 val NAMES = listOf("Alice", "Bob") val AGES = mapOf("Alice" to 35, "Bob" to 32) val COMMA_JOINED = NAMES.joinToString(", ") val EMPTY_ARRAY = arrayOf<SomeMutableType>()
این نامها معمولاً اسم یا عبارت اسمی هستند.
مقادیر ثابت فقط میتوانند در object
یا بهعنوان بیانیه سطح بالا تعریف شوند. مقادیری که درغیراینصورت الزامات ثابت را برآورده میکنند اما در class تعریف شدهاند باید از نام غیرثابت استفاده کنند.
ثابتهایی که مقادیر عددی دارند باید از const
اصلاحکننده استفاده کنند.
نامهای غیرثابت
نامهای غیرثابت بهصورت camelCase نوشته میشوند. این موارد برای داراییهای نمونه، داراییهای محلی، و نامهای پارامتر اعمال میشود.
val variable = "var" val nonConstScalar = "non-const" val mutableCollection: MutableSet<String> = HashSet() val mutableElements = listOf(mutableInstance) val mutableValues = mapOf("Alice" to mutableInstance, "Bob" to mutableInstance2) val logger = Logger.getLogger(MyClass::class.java.name) val nonEmptyArray = arrayOf("these", "can", "change")
این نامها معمولاً اسم یا عبارت اسمی هستند.
داراییهای پشتیبان
وقتی دارایی پشتیبان لازم باشد، نام آن باید دقیقاً با نام دارایی واقعی مطابقت داشته باشد بهجز اینکه با زیرخط پیشوندگذاری شود.
private var _table: Map<String, Int>? = null val table: Map<String, Int> get() { if (_table == null) { _table = HashMap() } return _table ?: throw AssertionError() }
نامهای متغیر را تایپ کنید
هر متغیر نوع به یکی از دو سبک نامگذاری میشود:
- یک حرف بزرگ، که بهصورت اختیاری با یک
عدد تکرقمی دنبال میشود (مثل
E،T،X،T2) - نامی در قالب استفادهشده برای کلاسها، بهدنبال آن حرف بزرگ
T(مثلRequestT،FooBarT)
حروف شتری
گاهی اوقات بیش از یک روش منطقی برای تبدیل عبارت انگلیسی به حروف شتری وجود دارد، مثلاً زمانی که سرواژهها یا ساختارهای غیرمعمول مانند «IPv6» یا «iOS» وجود دارند. برای بهبود پیشبینیپذیری، از طرح زیر استفاده کنید.
با شکل نثر نام شروع کنید:
- عبارت را به ASCII ساده تبدیل کنید و هرگونه آپوستروف را حذف کنید. برای مثال، «الگوریتم مولر» ممکن است به «الگوریتم مولرز» تبدیل شود.
- این نتیجه را به کلمات تقسیم کنید و براساس فاصلهها و نشانههای سجاوندی باقیمانده (معمولاً خط تیره) جدا کنید. توصیه میشود: اگر هر کلمهای در استفاده رایج ازقبل ظاهر شترینویسی متعارفی دارد، آن را به بخشهای تشکیلدهنده آن تقسیم کنید (برای نمونه، «AdWords» به «ad words» تبدیل میشود). توجه داشته باشید که کلمهای مثل «iOS» درواقع شترینویسی نیست؛ این کلمه از هر قرارداد متعارفی پیروی نمیکند، بنابراین این توصیه برای آن اعمال نمیشود.
- اکنون همه چیز (ازجمله سرواژهها) را به حروف کوچک تبدیل کنید، سپس یکی از کارهای زیر را انجام دهید:
- حرف اول هر کلمه را به حروف بزرگ تبدیل کنید تا به حالت پاسکال برسید.
- حرف اول هر کلمه بهجز کلمه اول را به حروف بزرگ تبدیل میکند تا حالت شتری ایجاد شود.
- درنهایت، همه کلمات را در یک شناسه واحد ادغام کنید.
توجه داشته باشید که حروف بزرگ و کوچک کلمات اصلی تقریباً بهطور کامل نادیده گرفته میشود.
| فرم نثر | تصحیح | نادرست |
|---|---|---|
| «درخواست XML Http» | XmlHttpRequest |
XMLHTTPRequest |
| «شناسه مشتری جدید» | newCustomerId |
newCustomerID |
| «کرونومتر داخلی» | innerStopwatch |
innerStopWatch |
| «از IPv6 در iOS پشتیبانی میکند» | supportsIpv6OnIos |
supportsIPv6OnIOS |
| «واردکننده YouTube» | YouTubeImporter |
YoutubeImporter* |
(* قابلقبول است، اما توصیه نمیشود.)
اسناد
قالببندی
قالببندی پایه بلوکهای KDoc در این مثال دیده میشود:
/** * Multiple lines of KDoc text are written here, * wrapped normally… */ fun method(arg: String) { // … }
…یا در این مثال تکخطی:
/** An especially short bit of KDoc. */
فرم پایه همیشه قابلقبول است. وقتی کل بلوک KDoc (ازجمله نشانگرهای نظر) در یک خط جا شود، ممکن است فرم تکخطی جایگزین شود. توجه داشته باشید که این فقط زمانی اعمال میشود که هیچ برچسب مسدودکنندهای مانند @return وجود نداشته باشد.
پاراگراف
یک خط خالی—یعنی خطی که فقط حاوی ستاره پیشرو همراستا
(*) است—بین پاراگرافها و قبلاز گروه برچسبهای بلوک درصورت وجود ظاهر میشود.
برچسبهای مسدودشده
هریک از «برچسبهای مسدودکننده» استاندارد که استفاده میشوند به ترتیب
@constructor، @receiver، @param، @property، @return،
@throws، @see ظاهر میشوند و هرگز با شرح خالی ظاهر نمیشوند.
وقتی برچسب بلوک در یک خط جا نمیشود،
خطوط ادامه با ۴ فاصله از موقعیت @ تورفتگی دارند.
بخش خلاصه
هر بلوک KDoc با یک قطعه خلاصه کوتاه شروع میشود. این تکهنوشت بسیار مهم است: این تنها بخشی از نوشتار است که در زمینههای خاصی مثل نمایههای کلاس و روش ظاهر میشود.
این یک جمله ناقص است–عبارت اسمی یا عبارت فعلی است، نه یک جمله کامل.
با «A `Foo` is a...» یا «This method returns...» شروع نمیشود،
و لازم نیست جمله امری کامل مثل
«Save the record.» را تشکیل دهد. بااینحال، این تکهنوشتار مانند یک جمله کامل با حروف بزرگ شروع میشود و
نشانهگذاری میشود.
کاربرد
حداقل، KDoc برای هر نوع public،
و هر عضو public یا protected از چنین نوعی وجود دارد،
با چند استثنا که در زیر ذکر شده است.
استثنا: توابع خودتوضیحی
KDoc برای توابع «ساده و واضح» مثل getFoo
و داراییهایی مثل foo اختیاری است، در مواردی که واقعاً و حقیقتاً چیز دیگری برای گفتن وجود ندارد جز «foo را برمیگرداند».
استناد به این استثنا برای توجیه حذف اطلاعات مربوطی که خواننده معمولی ممکن است نیاز به دانستن آن داشته باشد، مناسب نیست. برای مثال، برای
تابعی بهنام getCanonicalName یا دارایی بهنام canonicalName،
اگر خواننده معمولی ممکن است نداند
اصطلاح «نام متعارف» به چه معنا است، مستندات آن را (با این استدلال که فقط
/** Returns the canonical name. */ را میگوید) حذف نکنید!
استثنا: ملغی میکند
KDoc همیشه در روشی که روش نوع فرابالا را ملغی میکند وجود ندارد.