מדריך הסגנון של Kotlin

המסמך הזה הוא ההגדרה המלאה של תקני הקידוד של Google ל-Android עבור קוד מקור בשפת התכנות Kotlin. קובץ מקור של Kotlin מתואר כקובץ בסגנון Google Android רק אם הוא עומד בכללים שמופיעים כאן.

בדומה למדריכים אחרים לסגנון תכנות, הבעיות שמפורטות במדריך הזה לא מתייחסות רק לבעיות אסתטיות של עיצוב, אלא גם לסוגים אחרים של מוסכמות או תקני קידוד. עם זאת, המסמך הזה מתמקד בעיקר בכללים הברורים והמחייבים שאנחנו פועלים לפיהם באופן אוניברסלי, ולא כולל עצות שלא ניתן לאכוף בבירור (בין אם על ידי אדם או כלי).

העדכון האחרון: 2023-09-06

קובצי מקור

כל קובצי המקור צריכים להיות בקידוד 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 להזחה.

רצפי בריחה מיוחדים

לכל תו שיש לו רצף בריחה מיוחד (\b,‏ \n,‏ \r,‏ \t,‏ \',‏ \",‏ \\ ו-\$), נעשה שימוש ברצף הזה ולא ברצף הבריחה המתאים של Unicode (לדוגמה, \u000a).

תווים שאינם ASCII

לגבי שאר התווים שהם לא ASCII, משתמשים בתו Unicode בפועל (למשל, ) או בתו בריחה (escape) מקביל של Unicode (למשל, \u221e). הבחירה תלויה רק במה שיגרום לקוד להיות קל יותר לקריאה ולהבנה. לא מומלץ להשתמש בתווי escape של Unicode לתווים שניתנים להדפסה בכל מיקום, ולא מומלץ להשתמש בהם מחוץ למחרוזות מילוליות ולתגובות.

דוגמה דיון
val unitAbbrev = "μs" הכי טוב: ברור לחלוטין גם בלי תגובה.
val unitAbbrev = "\u03bcs" // μs גרוע: אין סיבה להשתמש בתו בריחה עם תו שאפשר להדפיס.
val unitAbbrev = "\u03bcs" איכות נמוכה: הקורא לא יודע מה זה.
return "\ufeff" + content טוב: משתמשים בתווי escape לתווים שאי אפשר להדפיס, ומוסיפים הערה אם צריך.

מבנה

קובץ .kt כולל את הרכיבים הבאים, לפי הסדר:

  • כותרת בנושא זכויות יוצרים או רישיון (אופציונלי)
  • הערות ברמת הקובץ
  • דף מידע על חבילה
  • ייבוא דפי פירוט חשבון
  • הצהרות ברמה העליונה

בין כל אחד מהקטעים האלה יש בדיוק שורה ריקה אחת.

אם צריך להוסיף כותרת של זכויות יוצרים או רישיון לקובץ, צריך למקם אותה בחלק העליון של הקובץ בתור הערה מרובת שורות.

/*
 * Copyright 2017 Google, Inc.
 *
 * ...
 */
 

אסור להשתמש בהערה בסגנון KDoc או בהערה בסגנון של שורה אחת.

/**
 * Copyright 2017 Google, Inc.
 *
 * ...
 */
// Copyright 2017 Google, Inc.
//
// ...

הערות ברמת הקובץ

הערות עם יעד לשימוש באתר מסוג file ממוקמות בין כל הערה של כותרת לבין הצהרת החבילה.

דף מידע על חבילה

הצהרת החבילה לא כפופה למגבלת עמודות, והיא אף פעם לא נשברת לשורה חדשה.

ייבוא דפי פירוט חשבון

הצהרות ייבוא של מחלקות, פונקציות ומאפיינים מקובצות יחד ברשימה אחת וממוינות לפי ASCII.

אסור לייבא תווים כלליים (מכל סוג).

בדומה להצהרת החבילה, הצהרות ייבוא לא כפופות למגבלת עמודות, והן אף פעם לא נשברות לשורות.

הצהרות ברמה העליונה

בקובץ .kt אפשר להצהיר על סוג אחד או יותר, על פונקציות, על מאפיינים או על כינויי סוגים ברמה העליונה.

התוכן של הקובץ צריך להתמקד בנושא אחד. דוגמאות לכך הן סוג ציבורי יחיד או קבוצה של פונקציות הרחבה שמבצעות את אותה פעולה על כמה סוגים של מקבלים. הצהרות לא קשורות צריכות להיות מופרדות לקבצים משלהן, והצהרות ציבוריות בתוך קובץ יחיד צריכות להיות ממוזערות.

אין הגבלה מפורשת על מספר הפריטים בקובץ או על הסדר שלהם.

בדרך כלל קוראים את קובצי המקור מלמעלה למטה, כלומר הסדר צריך לשקף את העובדה שההצהרות שמופיעות למעלה משפיעות על ההבנה של ההצהרות שמופיעות למטה. יכול להיות שקבצים שונים יסדרו את התוכן שלהם בצורה שונה. באופן דומה, קובץ אחד יכול להכיל 100 מאפיינים, קובץ אחר יכול להכיל 10 פונקציות וקובץ נוסף יכול להכיל מחלקה אחת.

מה שחשוב הוא שכל קובץ ישתמש בסדר לוגי כלשהו, שאפשר לקבל עליו הסבר מהאחראי על התחזוקה של הקובץ. לדוגמה, פונקציות חדשות לא מתווספות באופן קבוע לסוף הקובץ, כי אז הסדר יהיה "כרונולוגי לפי תאריך ההוספה", וזה לא סדר הגיוני.

סדר החברים בכיתה

הסדר של חברי הכיתה נקבע לפי אותם כללים שחלים על ההצהרות ברמה העליונה.

עיצוב

גשר בשיניים

אין צורך בסוגריים מסולסלים עבור 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 and Ritchie) ("סוגריים מצריים") עבור בלוקים לא ריקים ומבנים דמויי בלוקים:

  • אין מעבר שורה לפני הסוגר המסולסל הפותח.
  • מעבר שורה אחרי הסוגר המסולסל הפותח.
  • מעבר שורה לפני הסוגר המסולסל הסוגר.
  • מעבר שורה אחרי הסוגר המסולסל הסוגר, רק אם הסוגר הזה מסיים הצהרה או מסיים את הגוף של פונקציה, של constructor או של מחלקה בעלת שם. לדוגמה, אין מעבר שורה אחרי הסוגר המסולסל אם אחריו מופיע 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 classes.

בלוקים ריקים

בלוק ריק או מבנה דמוי בלוק צריך להיות בסגנון 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
}

כניסה מהשוליים

בכל פעם שפותחים בלוק חדש או מבנה דמוי בלוק, ההזחה גדלה בארבעה רווחים. כשהבלוק מסתיים, ההזחה חוזרת לרמת ההזחה הקודמת. רמת הכניסה חלה על הקוד ועל התגובות בכל הבלוק.

הצהרה אחת בכל שורה

אחרי כל הצהרה יש מעבר שורה. לא משתמשים בנקודה-פסיק.

גלישת שורות

הקוד מוגבל ל-100 תווים בעמודה. למעט מקרים שצוינו בהמשך, כל שורה שחורגת מהמגבלה הזו חייבת להיות שורה ארוכה שגולשת לשורה הבאה, כמו שמוסבר בהמשך.

חריגים:

  • שורות שבהן אי אפשר לציית למגבלת העמודות (לדוגמה, כתובת URL ארוכה ב-KDoc)
  • package ו-import
  • שורות פקודה בתגובה שאפשר לגזור ולהדביק במעטפת

איפה להוסיף מעבר שורה

הכלל העיקרי לגבי גלישת שורות הוא: עדיף ליצור שורה חדשה ברמה תחבירית גבוהה יותר. כמו כן:

  • כשמפצלים שורה באופרטור או בשם של פונקציית infix, הפיצול מתבצע אחרי האופרטור או השם של פונקציית ה-infix.
  • כששורה נשברת באחד מהסמלים הבאים שדומים לאופרטורים, השבירה מתרחשת לפני הסמל:
    • המפריד נקודה (., ?.).
    • שתי הנקודתיים של הפניה לחבר (::).
  • שם של שיטה או של בנאי נשאר צמוד לסוגר הפתוח (() שמופיע אחריו.
  • פסיק (,) נשאר צמוד לטוקן שלפניו.
  • חץ lambda‏ (->) נשאר צמוד לרשימת הארגומנטים שלפניו.

פונקציות

אם חתימת הפונקציה לא נכנסת לשורה אחת, צריך להעביר כל הצהרת פרמטר לשורה נפרדת. פרמטרים שמוגדרים בפורמט הזה צריכים להיות מוזחים ברווח אחד (+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 מוסברות בהמשך.
  • בין ההצהרות, במקרה הצורך, כדי לארגן את הקוד לחלקים לוגיים.
  • אופציונלית לפני ההצהרה הראשונה בפונקציה, לפני החבר הראשון במחלקה או אחרי החבר האחרון במחלקה (לא מומלץ ולא נאסר).
  • כפי שנדרש בסעיפים אחרים במסמך הזה (למשל, בסעיף Structure).

מותר להשתמש בכמה שורות ריקות רצופות, אבל לא מומלץ לעשות זאת ואין בכך צורך.

לרוחב

בנוסף למקומות שבהם נדרש רווח לפי כללי השפה או כללי סגנון אחרים, ובנוסף לערכים מילוליים, להערות ול-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
    ההנחיה הזו רלוונטית גם לסמלים הבאים שדומים לאופרטורים:
    • החץ בביטוי Lambda‏ (->).
      // 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 בלי פונקציות ובלי תיעוד של הקבועים שלו כשורה אחת.

enum class Answer { YES, NO, MAYBE }

כשמציבים את הקבועים בספירה בשורות נפרדות, לא צריך להוסיף שורה ריקה ביניהם, אלא אם הם מגדירים גוף.

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() {
    // …
}

אפשר להשתמש בתחביר @[...] רק עם יעד שימוש באתר מפורש, ורק כדי לשלב 2 הערות או יותר ללא ארגומנטים בשורה אחת.

@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 הן בפורמט PascalCase והשם שלהן הוא שם עצם, כאילו הן סוגים.

@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 modifier.

שמות לא קבועים

שמות לא קבועים נכתבים ב-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()
    }

הקלדת שמות של משתנים

לכל משתנה מסוג type יש שם באחד משני הסגנונות הבאים:

  • אות גדולה אחת, שאחריה יכול להיות מספר אחד (למשל E, T, X, T2)
  • שם בטופס שמשמש לכיתות, ואחריו האות הגדולה T (למשל RequestT, FooBarT)

קאמל קייס

לפעמים יש יותר מדרך סבירה אחת להמיר ביטוי באנגלית ל-Camel Case, למשל כשמופיעים ראשי תיבות או מבנים לא רגילים כמו IPv6 או iOS. כדי לשפר את יכולת החיזוי, מומלץ להשתמש בתרשים הבא.

מתחילים עם השם בצורת פרוזה:

  1. ממירים את הביטוי ל-ASCII פשוט ומסירים את כל הגרשיים. לדוגמה, 'האלגוריתם של מילר' יכול להפוך ל-'האלגוריתם של מילר'.
  2. מחלקים את התוצאה למילים, ומפצלים לפי רווחים וסימני פיסוק שנותרו (בדרך כלל מקפים). מומלץ: אם מילה כלשהי כבר מופיעה בשימוש הנפוץ בפורמט camel-case, צריך לפצל אותה לחלקים שמרכיבים אותה (לדוגמה, AdWords הופך ל-ad words). שימו לב שמילה כמו iOS לא באמת מופיעה בפורמט camel-case; היא לא עומדת באף מוסכמה, ולכן ההמלצה הזו לא רלוונטית.
  3. עכשיו צריך להפוך את כל האותיות לקטנות (כולל ראשי תיבות), ואז לבצע אחת מהפעולות הבאות:
    • האות הראשונה של כל מילה תהיה גדולה, כדי ליצור PascalCase.
    • הפונקציה משתמשת באותיות רישיות בתו הראשון של כל מילה, מלבד המילה הראשונה, כדי ליצור אותיות Camel Case.
  4. לבסוף, משלבים את כל המילים למזהה אחד.

שימו לב שהרישיות של המילים המקוריות כמעט ולא משנה.

טופס פרוזה תקין שגוי
‫"XML Http Request" XmlHttpRequest XMLHTTPRequest
‫"new customer ID" (מספר לקוח חדש) newCustomerId newCustomerID
'שעון העצר הפנימי' innerStopwatch innerStopWatch
‫"supports IPv6 on iOS" ‏(תמיכה ב-IPv6 ב-iOS) supportsIpv6OnIos supportsIPv6OnIOS
‫YouTube importer (כלי לייבוא מ-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, והם אף פעם לא מופיעים עם תיאור ריק. אם תג בלוק לא נכנס לשורה אחת, שורות ההמשך מוזחות ב-4 רווחים מהמיקום של @.

קטע סיכום

כל בלוק 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 לא תמיד מופיעה בשיטה שמבטלת שיטה של סוג-על.