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

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

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

עדכון אחרון: 6.09.2023

קובצי מקור

כל קובצי המקור חייבים להיות מקודדים לפי UTF-8.

מתן שמות

אם קובץ מקור מכיל רק מחלקה אחת ברמה העליונה, שם הקובץ צריך לשקף את השם תלוי אותיות רישיות והתוסף .kt. אחרת, אם קובץ מקור מכיל כמה הצהרות ברמה העליונה, צריך לבחור שם שמתאר את תוכן הקובץ, החל את PascalCase (camelCase היא קביל אם שם הקובץ הוא רבים), ומצרפים את הסיומת .kt.

// MyClass.kt
class MyClass { }
// Bar.kt
class Bar { }
fun Runnable.toBar(): Bar = // …
// Map.kt
fun <T, O> Set<T>.map(func: (T) -> O): List<O> = // …
fun <T, O> List<T>.map(func: (T) -> O): List<O> = // …
// extensions.kt
fun MyClass.process() = // …
fun MyResult.print() = // …

תווים מיוחדים

תווי רווח לבן

חוץ מרצף של מסיים הקו, תו רווח אופקי ב-ASCII (0x20) הוא תו הרווח הלבן היחיד שמופיע במקום כלשהו בקובץ המקור. כלומר:

  • כל שאר תווי הרווחים הלבנים בליטרל של מחרוזות ובמילים של תווים מסומנים בתו בריחה.
  • תווי Tab לא משמשים לכניסת פסקה.

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

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

תווים שאינם ASCII

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

דוגמה דיון
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.
//
// ...

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

הערות עם ה'קובץ' שימוש בטירגוט לפי אתר מוצבות בין כל תגובה לכותרת לבין הצהרת החבילה.

דוח החבילה

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

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

הצהרות ייבוא של מחלקות, פונקציות ומאפיינים מקובצים יחד ברשימה אחת ומיון 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)
}

בלוקים לא ריקים

סוגריים מסולסלים תואמים לסגנון קרניתן וריצ'י ("סוגריים מצריים") בלוקים לא ריקים ומבנים דמויי בלוקים:

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

בלוקים ריקים

בלוק ריק או מבנה דמוי בלוק חייב להיות בסגנון 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
  • את שורות הפקודה בתגובה שאפשר לגזור ולהדביק אותן במעטפת

איפה להפסיק

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

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

רווח לבן

אנכי

תופיע שורה ריקה אחת:

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

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

לרוחב

מעבר למה שנדרש על פי השפה או כללי סגנון אחרים, וחוץ מליטרלים, הערות ו-KDoc, גם במקומות הבאים בלבד:

  • צריך להפריד מילים שמורות, כמו 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> 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() {
    // …
}

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

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

שמות הפונקציות

שמות הפונקציות נכתבים בקאמל קייס, והם בדרך כלל פועלים או ביטויים של פועלים. לדוגמה, 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_JOINER = Joiner.on(',') // Joiner is immutable
val EMPTY_ARRAY = arrayOf()

שמות אלה הם בדרך כלל שמות עצם או ביטויים של שמות עצם.

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

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

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

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

val variable = "var"
val nonConstScalar = "non-const"
val mutableCollection: MutableSet = 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? = null

val table: Map
    get() {
        if (_table == null) {
            _table = HashMap()
        }
        return _table ?: throw AssertionError()
    }

צריך להקליד שמות של משתנים

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

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

כיסוי לגמל

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

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

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

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

צורת Prose תקין שגוי
"בקשת 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 והם אף פעם לא מופיעים עם תיאור ריק. כשתג בלוק לא מתאים לשורה אחת, קווי המשך מוכנסים במרחק של 4 רווחים מהמיקום של @.

קטע סיכום

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

הטקסט הזה הוא חלק מביטוי, שהוא ביטוי של שם עצם או של פועל, ולא משפט שלם. הוא לא מתחיל ב-"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 לא תמיד נמצא בשיטה שמבטלת שיטת Supertype.