این سند به عنوان تعریف کاملی از استانداردهای کدنویسی اندروید گوگل برای کد منبع در زبان برنامهنویسی کاتلین عمل میکند. یک فایل منبع کاتلین در صورتی به عنوان «سبک اندروید گوگل» توصیف میشود که از قوانین اینجا پیروی کند.
مانند سایر راهنماهای سبک برنامهنویسی، موضوعات مطرحشده نه تنها مسائل زیباییشناختی قالببندی، بلکه انواع دیگر قراردادها یا استانداردهای کدنویسی را نیز در بر میگیرد. با این حال، این سند در درجه اول بر قوانین سخت و سریعی که ما به طور جهانی از آنها پیروی میکنیم تمرکز دارد و از ارائه توصیههایی که به وضوح قابل اجرا نیستند (چه توسط انسان و چه توسط ابزار) خودداری میکند.
فایلهای منبع
تمام فایلهای منبع باید به صورت 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) تنها کاراکتر فاصله سفید است که در هر جایی از یک فایل منبع ظاهر میشود. این نشان میدهد که:
- تمام کاراکترهای فضای خالی دیگر در رشتهها و حروف الفبا، escape میشوند.
- از کاراکترهای Tab برای ایجاد تورفتگی استفاده نمیشود .
توالیهای فرار ویژه
برای هر کاراکتری که توالی escape خاصی دارد ( \b ، \n ، \r ، \t ، \' ، \" ، \\ ، و \$ )، آن توالی به جای escape یونیکد مربوطه (مثلاً \u000a ) استفاده میشود.
کاراکترهای غیر ASCII
برای بقیه کاراکترهای غیر ASCII، یا از کاراکتر یونیکد واقعی (مثلاً ∞ ) یا از escape معادل یونیکد (مثلاً \u221e ) استفاده میشود. انتخاب فقط به این بستگی دارد که کدام یک خواندن و درک کد را آسانتر میکند. escapeهای یونیکد برای کاراکترهای قابل چاپ در هر مکانی توصیه نمیشوند و به جز رشتههای تحتاللفظی و کامنتها، اکیداً توصیه نمیشوند.
| مثال | بحث |
|---|---|
val unitAbbrev = "μs" | بهترین: کاملاً واضح حتی بدون هیچ توضیحی. |
val unitAbbrev = "\u03bcs" // μs | ضعیف: هیچ دلیلی برای استفاده از escape با یک کاراکتر قابل چاپ وجود ندارد. |
val unitAbbrev = "\u03bcs" | بیچاره: خواننده هیچ ایدهای ندارد که این چیست. |
return "\ufeff" + content | خوب: برای کاراکترهای غیرقابل چاپ از escape استفاده کنید و در صورت لزوم کامنت بگذارید. |
ساختار
یک فایل .kt به ترتیب شامل موارد زیر است:
- سربرگ حق نشر و/یا مجوز (اختیاری)
- حاشیهنویسیهای سطح فایل
- بیانیه بسته
- وارد کردن اظهارات
- اعلامیههای سطح بالا
دقیقاً یک خط خالی هر یک از این بخشها را از هم جدا میکند.
حق نشر / مجوز
اگر عنوان حق نشر یا مجوز به فایل تعلق دارد، باید در بالای آن و در یک کامنت چندخطی قرار گیرد.
/* * Copyright 2017 Google, Inc. * * ... */
از کامنتهای به سبک KDoc یا کامنتهای تکخطی استفاده نکنید.
/** * Copyright 2017 Google, Inc. * * ... */
// Copyright 2017 Google, Inc. // // ...
حاشیهنویسیهای سطح فایل
حاشیهنویسیهایی با هدف use-site از نوع "file" بین هر کامنت هدر و اعلان پکیج قرار میگیرند.
بیانیه بسته
دستور package تابع هیچ محدودیت ستونی نیست و هرگز به صورت خطی (line-wrapped) نوشته نمیشود.
وارد کردن اظهارات
دستورات ایمپورت برای کلاسها، توابع و ویژگیها در یک لیست واحد گروهبندی شده و به صورت ASCII مرتب میشوند.
واردات Wildcard (از هر نوع) مجاز نیست.
مشابه دستور package، دستورات import محدودیت ستون ندارند و هرگز به صورت خطی (line-wrapped) نوشته نمیشوند.
اعلامیههای سطح بالا
یک فایل .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) }
بلوکهای غیر خالی
پرانتزها برای بلوکهای غیرتهی و سازههای بلوکمانند از سبک کرنیگان و ریچی ("براکتهای مصری") پیروی میکنند:
- قبل از آکولاد آغازین، هیچ خط فاصلهای وجود ندارد.
- شکست خط بعد از آکولاد آغازین.
- قبل از آکولاد پایانی، خط جدید ایجاد کنید.
- بعد از براکت بسته، فقط در صورتی که آن براکت یک دستور را خاتمه دهد یا بدنه یک تابع، سازنده یا کلاس نامگذاری شده را خاتمه دهد، خط جدید ایجاد میشود. برای مثال، اگر بعد از براکت،
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() } } }
چند استثنا برای کلاسهای enum در زیر آورده شده است.
بلوکهای خالی
یک بلوک خالی یا سازهای شبیه به بلوک باید به سبک 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 }
تورفتگی
هر بار که یک بلوک یا ساختار شبیه به بلوک جدید باز میشود، تورفتگی به اندازه چهار فاصله افزایش مییابد. وقتی بلوک به پایان میرسد، تورفتگی به سطح تورفتگی قبلی برمیگردد. سطح تورفتگی هم برای کد و هم برای توضیحات در سراسر بلوک اعمال میشود.
یک دستور در هر خط
بعد از هر دستور، یک خط جدید قرار میگیرد. از نقطه ویرگول استفاده نمیشود.
بسته بندی خط
کد دارای محدودیت ستون ۱۰۰ کاراکتری است. به جز مواردی که در زیر ذکر شده است، هر خطی که از این محدودیت تجاوز کند، باید همانطور که در زیر توضیح داده شده است، به صورت خطی بستهبندی شود.
استثنائات:
- خطوطی که رعایت محدودیت ستون در آنها امکانپذیر نیست (برای مثال، یک URL طولانی در KDoc)
- دستورات
packageوimport - خطوط فرمان در یک کامنت که میتوان آنها را برش داد و در یک پوسته جایگذاری کرد
کجا بشکنیم؟
دستورالعمل اصلیِ بستهبندی خط این است: ترجیح میدهیم در سطح نحوی بالاتری بشکنیم. همچنین:
- وقتی یک خط در محل یک عملگر یا میانوند نام تابع شکسته میشود، نقطه شکست بعد از نام عملگر یا میانوند نام تابع میآید.
- وقتی یک خط در نمادهای «عملگر-مانند» زیر شکسته میشود، نقطه شکست قبل از نماد میآید:
- جداکننده نقطه (
.،?.). - دو دونقطه از یک ارجاع عضو (
::)
- جداکننده نقطه (
- نام یک متد یا سازنده به پرانتز باز
() که پس از آن قرار میگیرد، متصل میماند. - یک کاما (
,) به توکنی که قبل از آن میآید، متصل میماند. - یک فلش لامبدا (
->) به لیست آرگومانهایی که قبل از آن میآیند، متصل میماند.
توابع
وقتی امضای یک تابع در یک خط جا نمیشود، هر اعلان پارامتر را در خط خودش قرار دهید. پارامترهای تعریف شده در این قالب باید از یک تورفتگی (+4) استفاده کنند. پرانتز بسته ( ) و نوع بازگشتی بدون هیچ تورفتگی اضافی در خط خودشان قرار میگیرند.
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 را تعریف میکنند، باید هر کدام را در خط خودشان با یک تورفتگی معمولی (+4) قرار دهند. آنها را با استفاده از همان قوانین توابع قالببندی کنید.
var directory: File? = null set(value) { // … }
val defaultExtension: String get() = "kt"
فضای سفید
عمودی
یک خط خالی ظاهر میشود:
- بین اعضای متوالی یک کلاس: ویژگیها، سازندهها، توابع، کلاسهای تو در تو و غیره.
- استثنا: یک خط خالی بین دو ویژگی متوالی (بدون کد دیگری بین آنها) اختیاری است. چنین خطوط خالی در صورت نیاز برای ایجاد گروهبندیهای منطقی از ویژگیها و مرتبط کردن ویژگیها با ویژگی پشتیبان خود، در صورت وجود، استفاده میشوند.
- استثنا: خطوط خالی بین ثابتهای enum در زیر توضیح داده شده است.
- بین دستورات، در صورت نیاز برای سازماندهی کد به زیربخشهای منطقی.
- به صورت اختیاری قبل از اولین دستور در یک تابع، قبل از اولین عضو یک کلاس، یا بعد از آخرین عضو یک کلاس (نه تشویق شده و نه دلسرد شده).
- همانطور که در سایر بخشهای این سند (مانند بخش ساختار ) الزامی شده است.
چندین خط خالی متوالی مجاز است، اما توصیه یا الزامی نیست.
افقی
فراتر از مواردی که توسط زبان یا سایر قوانین سبک لازم است، و جدا از حروف، نظرات و 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 بدون هیچ تابع و مستندسازی در مورد ثابتهایش، میتواند به صورت اختیاری به صورت یک خط قالببندی شود.
enum class Answer { YES, NO, MAYBE }
وقتی ثابتهای یک enum در سطرهای جداگانه قرار میگیرند، نیازی به گذاشتن خط خالی بین آنها نیست، مگر در مواردی که یک بدنه را تعریف کنند.
enum class Answer { YES, NO, MAYBE { override fun toString() = """¯\_(ツ)_/¯""" } }
از آنجایی که کلاسهای enum کلاس هستند، سایر قوانین مربوط به قالببندی کلاسها نیز اعمال میشوند.
حاشیهنویسیها
حاشیهنویسیهای عضو یا نوع، بلافاصله قبل از ساختار حاشیهنویسیشده، در خطوط جداگانه قرار میگیرند.
@Retention(SOURCE) @Target(FUNCTION, PROPERTY_SETTER, FIELD) annotation class Global
حاشیهنویسیهای بدون آرگومان را میتوان در یک خط قرار داد.
@JvmField @Volatile var disposable: Disposable? = null
وقتی فقط یک حاشیهنویسی بدون آرگومان وجود دارد، میتوان آن را در همان خطی که اعلان تابع قرار دارد، قرار داد.
@Volatile var disposable: Disposable? = null @Test fun selectAll() { // … }
سینتکس @[...] فقط میتواند با یک هدف صریح use-site و فقط برای ترکیب ۲ یا چند حاشیهنویسی بدون آرگومان در یک خط استفاده شود.
@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) { // … }
نام توابع نباید شامل فاصله باشد زیرا این ویژگی در همه پلتفرمها پشتیبانی نمیشود (به ویژه در اندروید به طور کامل پشتیبانی نمیشود).
// 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» واقعاً به خودی خود در حروف شتری نیست؛ این کلمه از هرگونه قراردادی سرپیچی میکند، بنابراین این توصیه اعمال نمیشود.
- حالا همه چیز (از جمله کلمات اختصاری) را با حروف کوچک بنویسید، سپس یکی از کارهای زیر را انجام دهید:
- برای تبدیل به حالت پاسکال، اولین کاراکتر هر کلمه را با حروف بزرگ بنویسید.
- اولین کاراکتر هر کلمه را با حروف بزرگ بنویسید، به جز اولین کاراکتری که حالت شتری (camel case) را ایجاد میکند.
- در نهایت، تمام کلمات را در یک شناسه واحد ادغام کنید.
توجه داشته باشید که تقریباً به طور کامل از حروف بزرگ و کوچک کلمات اصلی صرف نظر شده است.
| قالب نثر | درست | نادرست |
|---|---|---|
| "درخواست XML Http" | XmlHttpRequest | XMLHTTPRequest |
| «شناسه مشتری جدید» | newCustomerId | newCustomerID |
| "کرونومتر داخلی" | innerStopwatch | innerStopWatch |
| «پشتیبانی از IPv6 در iOS» | supportsIpv6OnIos | supportsIPv6OnIOS |
| «واردکنندهی یوتیوب» | 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 همیشه در متدی که یک متد سوپرتایپ را لغو میکند، وجود ندارد.