คู่มือแนะนำเกี่ยวกับ Kotlin

เอกสารนี้เป็นคำจำกัดความที่สมบูรณ์ของมาตรฐานการเขียนโค้ด Android ของ Google สำหรับซอร์สโค้ดในภาษาการเขียนโปรแกรม 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) เป็นอักขระช่องว่างเพียงตัวเดียวที่ปรากฏที่ใดก็ได้ในไฟล์ต้นฉบับ ซึ่งหมายความว่า

  • ระบบจะกำหนดอักขระหลีกให้กับอักขระช่องว่างอื่นๆ ทั้งหมดในสตริงและอักขระตามตัว
  • ไม่ใช้อักขระแท็บสำหรับการเยื้อง

ลำดับหลีกพิเศษ

สำหรับอักขระใดๆ ที่มีลำดับการหลีกพิเศษ (\b, \n, \r, \t, \', \", \\ และ \$) จะใช้ลำดับดังกล่าวแทนการหลีก Unicode ที่เกี่ยวข้อง (เช่น \u000a)

อักขระที่ไม่ใช่ ASCII

สำหรับอักขระที่ไม่ใช่ ASCII ที่เหลือ จะใช้อักขระ Unicode จริง (เช่น ) หรืออักขระหลีก Unicode ที่เทียบเท่า (เช่น \u221e) การเลือกขึ้นอยู่กับว่าตัวเลือกใดจะช่วยให้อ่านและทำความเข้าใจโค้ดได้ง่ายขึ้น เราไม่แนะนำให้ใช้การหลีก Unicode สำหรับอักขระที่พิมพ์ได้ในทุกตำแหน่ง และ ไม่แนะนำอย่างยิ่งให้ใช้ภายนอกสตริงลิเทอรัลและความคิดเห็น

ตัวอย่าง การอภิปรายผล
val unitAbbrev = "μs" ดีที่สุด: ชัดเจนอย่างยิ่งแม้ไม่มีความคิดเห็น
val unitAbbrev = "\u03bcs" // μs ไม่ดี: ไม่ควรใช้การหลีกที่มีอักขระที่พิมพ์ได้
val unitAbbrev = "\u03bcs" แย่: ผู้อ่านไม่รู้ว่านี่คืออะไร
return "\ufeff" + content ดี: ใช้การหลีกสำหรับอักขระที่พิมพ์ไม่ได้ และแสดงความคิดเห็นหากจำเป็น

โครงสร้าง

ไฟล์ .kt ประกอบด้วยข้อมูลต่อไปนี้ตามลำดับ

  • ส่วนหัวของลิขสิทธิ์และ/หรือใบอนุญาต (ไม่บังคับ)
  • คำอธิบายประกอบระดับไฟล์
  • ใบแจ้งยอดแพ็กเกจ
  • นำเข้าใบแจ้งยอด
  • ประกาศระดับบนสุด

แต่ละส่วนเหล่านี้จะคั่นด้วยบรรทัดว่าง 1 บรรทัด

หากส่วนหัวของลิขสิทธิ์หรือใบอนุญาตอยู่ในไฟล์ ควรวางไว้ที่ด้านบนสุดในความคิดเห็นแบบหลายบรรทัด

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

อย่าใช้ความคิดเห็นรูปแบบ KDoc หรือรูปแบบบรรทัดเดียว

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

คำอธิบายประกอบระดับไฟล์

คำอธิบายประกอบที่มีเป้าหมาย use-site ของ "file" จะอยู่ระหว่างความคิดเห็นส่วนหัวและการประกาศแพ็กเกจ

ใบแจ้งยอดแพ็กเกจ

โดยข้อความในแพ็กเกจจะไม่มีการจำกัดคอลัมน์และจะไม่มีการตัดข้อความ

นำเข้าใบแจ้งยอด

คำสั่งนำเข้าสำหรับคลาส ฟังก์ชัน และพร็อพเพอร์ตี้จะจัดกลุ่มไว้ในรายการเดียวและจัดเรียงตาม ASCII

ไม่อนุญาตให้นำเข้าไวลด์การ์ด (ทุกประเภท)

เช่นเดียวกับคำสั่งแพ็กเกจ คำสั่งนำเข้าจะไม่มี ขีดจำกัดคอลัมน์และจะไม่มีการตัดคำ

ประกาศระดับบนสุด

.ktไฟล์สามารถประกาศประเภท ฟังก์ชัน พร็อพเพอร์ตี้ หรือนามแฝงประเภทอย่างน้อย 1 รายการที่ระดับบนสุด

เนื้อหาของไฟล์ควรเน้นที่ธีมเดียว ตัวอย่างของกรณีนี้ คือประเภทสาธารณะเดียวหรือชุดฟังก์ชันส่วนขยายที่ดำเนินการ การดำเนินการเดียวกันในตัวรับหลายประเภท การประกาศที่ไม่เกี่ยวข้องควรแยกเป็นไฟล์ของตัวเอง และควรลดการประกาศแบบสาธารณะภายในไฟล์เดียวให้เหลือน้อยที่สุด

ไม่มีการจำกัดจำนวนหรือลำดับของเนื้อหาในไฟล์อย่างชัดเจน

โดยปกติแล้ว ระบบจะอ่านไฟล์ต้นฉบับจากบนลงล่าง ซึ่งหมายความว่าโดยทั่วไปแล้วลำดับควรแสดงให้เห็นว่าการประกาศที่อยู่ด้านบนจะช่วยให้เข้าใจการประกาศที่อยู่ด้านล่างได้ ไฟล์ต่างๆ อาจเลือกจัดเรียงเนื้อหาแตกต่างกัน ในทำนองเดียวกัน ไฟล์หนึ่งอาจมีพร็อพเพอร์ตี้ 100 รายการ อีกไฟล์หนึ่งมีฟังก์ชัน 10 รายการ และอีกไฟล์หนึ่งมีคลาสเดียว

สิ่งสำคัญคือแต่ละไฟล์ต้องใช้ลำดับเชิงตรรกะบางอย่าง ซึ่งผู้ดูแลรักษาจะอธิบายได้หากมีการสอบถาม เช่น ฟังก์ชันใหม่ๆ จะไม่ได้เพิ่มไว้ที่ท้ายไฟล์ตามปกติ เนื่องจากจะทำให้การจัดเรียงเป็น "ตามลำดับเวลา ตามวันที่เพิ่ม" ซึ่งไม่ใช่การจัดเรียงเชิงตรรกะ

การจัดลำดับสมาชิกในชั้นเรียน

ลำดับของสมาชิกภายในคลาสจะเป็นไปตามกฎเดียวกับการประกาศระดับบนสุด

การจัดรูปแบบ

เหล็กดัดฟัน

ไม่จำเป็นต้องใช้เครื่องหมายปีกกาสำหรับwhenกิ่งก้านและifนิพจน์ ซึ่งมีelseกิ่งก้านไม่เกิน 1 รายการและพอดีกับบรรทัดเดียว

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()
        }
    }
}

ด้านล่างนี้คือข้อยกเว้นบางส่วนสำหรับ คลาส 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
}

การเยื้อง

ทุกครั้งที่เปิดบล็อกใหม่หรือโครงสร้างที่คล้ายบล็อก การเยื้องจะเพิ่มขึ้น 4 ช่องว่าง เมื่อบล็อกสิ้นสุดลง การเยื้องจะกลับไปที่ระดับการเยื้องก่อนหน้า ระดับการเยื้องจะมีผลกับทั้งโค้ดและความคิดเห็นตลอดทั้งบล็อก

1 คำสั่งต่อบรรทัด

โดยแต่ละคำสั่งจะตามด้วยการขึ้นบรรทัดใหม่ โดยไม่ต้องใช้เครื่องหมายอัฒภาค

การตัดบรรทัด

โค้ดมีขีดจำกัดคอลัมน์ที่ 100 อักขระ บรรทัดใดก็ตามที่เกินขีดจำกัดนี้จะต้องมีการตัดบรรทัดตามที่อธิบายไว้ด้านล่าง ยกเว้นตามที่ระบุไว้ด้านล่าง

ข้อยกเว้น:

  • บรรทัดที่ทำตามขีดจำกัดของคอลัมน์ไม่ได้ (เช่น URL ยาวใน KDoc)
  • ใบแจ้งยอด package และ import
  • บรรทัดคำสั่งในความคิดเห็นที่อาจมีการคัดลอกและวางลงในเชลล์

ตำแหน่งที่ควรขึ้นบรรทัดใหม่

หลักการสำคัญของการตัดบรรทัดคือให้ตัดที่ระดับไวยากรณ์ที่สูงกว่า นอกจากนี้ การโอนช่องยังมีผลในด้านต่างๆ ต่อไปนี้

  • เมื่อมีการขึ้นบรรทัดใหม่ที่โอเปอเรเตอร์หรือชื่อฟังก์ชัน Infix การขึ้นบรรทัดใหม่จะอยู่ หลังโอเปอเรเตอร์หรือชื่อฟังก์ชัน Infix
  • เมื่อมีการขึ้นบรรทัดใหม่ที่สัญลักษณ์ "คล้ายโอเปอเรเตอร์" ต่อไปนี้ การขึ้นบรรทัดใหม่ จะอยู่ก่อนสัญลักษณ์
    • ตัวคั่นจุด (., ?.)
    • เครื่องหมายโคลอน 2 ตัวของการอ้างอิงสมาชิก (::)
  • ชื่อเมธอดหรือชื่อตัวสร้างจะติดอยู่กับวงเล็บเปิด (() ที่อยู่ ต่อจากชื่อนั้น
  • เครื่องหมายคอมมา (,) จะติดอยู่กับโทเค็นที่อยู่ก่อนหน้า
  • ลูกศร 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"

เว้นวรรค

ประเภทธุรกิจ

บรรทัดว่างจะปรากฏขึ้น

  • ระหว่างสมาชิกที่อยู่ติดกันของคลาส ได้แก่ พร็อพเพอร์ตี้ คอนสตรัคเตอร์ ฟังก์ชัน คลาสที่ซ้อนกัน ฯลฯ
    • ข้อยกเว้น: บรรทัดว่างระหว่างพร็อพเพอร์ตี้ 2 รายการ ที่ต่อเนื่องกัน (ไม่มีโค้ดอื่นอยู่ระหว่างพร็อพเพอร์ตี้ทั้ง 2 รายการ) เป็นตัวเลือก ระบบจะใช้บรรทัดว่างดังกล่าวตามความจำเป็นเพื่อสร้าง การจัดกลุ่มพร็อพเพอร์ตี้เชิงตรรกะและเชื่อมโยงพร็อพเพอร์ตี้ กับพร็อพเพอร์ตี้สำรอง หากมี
    • ข้อยกเว้น: บรรทัดว่างระหว่างค่าคงที่ของ 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()) {
    }
  • ทั้ง 2 ด้านของตัวดำเนินการไบนารี
    // WRONG!
    val two = 1+1
    // Okay
    val two = 1 + 1
    นอกจากนี้ ยังมีผลกับสัญลักษณ์ที่ "คล้ายโอเปอเรเตอร์" ต่อไปนี้ด้วย
    • ลูกศรในนิพจน์ Lambda (->)
      // WRONG!
      ints.map { value->value.toString() }
      // Okay
      ints.map { value -> value.toString() }
    แต่ไม่ใช่:
    • เครื่องหมายโคลอน 2 ตัว (::) ของการอ้างอิงสมาชิก
      // 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
  • ทั้ง 2 ด้านของเครื่องหมายทับคู่ (//) ที่เริ่มต้นความคิดเห็นท้ายบรรทัด ในที่นี้ คุณจะเว้นวรรคหลายครั้งได้ แต่ไม่จำเป็น
    // WRONG!
    var debugging = false//disabled by default
    // Okay
    var debugging = false // disabled by default

กฎนี้ไม่เคยตีความว่าต้องมีหรือห้ามมี ช่องว่างเพิ่มเติมที่จุดเริ่มต้นหรือจุดสิ้นสุดของบรรทัด แต่จะใช้กับ ช่องว่างภายในเท่านั้น

โครงสร้างที่เฉพาะเจาะจง

คลาส Enum

คุณอาจจัดรูปแบบ 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() {
    // …
}

@[...] ใช้ได้กับเป้าหมายการใช้เว็บไซต์ที่ชัดเจนเท่านั้น และใช้ได้เฉพาะในกรณีที่ รวมคำอธิบายประกอบตั้งแต่ 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 จะเป็น 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()
    }

พิมพ์ชื่อตัวแปร

ตัวแปรประเภทแต่ละรายการจะตั้งชื่อในรูปแบบใดรูปแบบหนึ่งต่อไปนี้

  • ตัวอักษรตัวพิมพ์ใหญ่ 1 ตัว ตามด้วยตัวเลข 1 ตัว (ไม่บังคับ) (เช่น E, T, X, T2)
  • ชื่อในแบบฟอร์มที่ใช้สำหรับชั้นเรียน ตามด้วยตัวอักษรตัวพิมพ์ใหญ่ T (เช่น RequestT, FooBarT)

Camel Case

บางครั้งอาจมีวิธีที่สมเหตุสมผลมากกว่า 1 วิธีในการแปลงวลีภาษาอังกฤษเป็นรูปแบบ Camel Case เช่น เมื่อมีคำย่อหรือโครงสร้างที่ผิดปกติ เช่น "IPv6" หรือ "iOS" ใช้รูปแบบต่อไปนี้เพื่อปรับปรุงความสามารถในการคาดการณ์

เริ่มต้นด้วยรูปแบบร้อยแก้วของชื่อ

  1. แปลงวลีเป็น ASCII แบบธรรมดาและนำเครื่องหมายอัญประกาศออก เช่น "อัลกอริทึมของมึลเลอร์" อาจกลายเป็น "อัลกอริทึมของมึลเลอร์"
  2. แบ่งผลลัพธ์นี้ออกเป็นคำ โดยแยกตามช่องว่างและเครื่องหมายวรรคตอนที่เหลือ (โดยปกติคือขีดกลาง) แนะนำ: หากคำใดมีรูปแบบ Camel Case ตามธรรมเนียมในการใช้งานทั่วไป ให้แยกคำนั้นออกเป็นส่วนประกอบ (เช่น "AdWords" จะกลายเป็น "ad words") โปรดทราบว่าคำอย่างเช่น "iOS" ไม่ได้อยู่ในรูปแบบ Camel Case โดยแท้จริง แต่เป็นการละเมิดธรรมเนียม ดังนั้นคำแนะนำนี้จึงไม่มีผล
  3. จากนั้นเปลี่ยนตัวอักษรทั้งหมดเป็นตัวพิมพ์เล็ก (รวมถึงตัวย่อ) แล้วทำอย่างใดอย่างหนึ่งต่อไปนี้
    • เปลี่ยนอักขระแรกของแต่ละคำเป็นตัวพิมพ์ใหญ่เพื่อให้ได้รูปแบบ Pascal Case
    • เปลี่ยนอักขระแรกของแต่ละคำเป็นตัวพิมพ์ใหญ่ ยกเว้นคำแรก เพื่อให้ได้รูปแบบ Camel Case
  4. สุดท้าย ให้รวมคำทั้งหมดเป็นตัวระบุเดียว

โปรดทราบว่าระบบจะไม่สนใจการใช้อักษรตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ของคำเดิมเกือบทั้งหมด

รูปแบบร้อยแก้ว ถูกต้อง ไม่ถูกต้อง
"XML Http Request" 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

ย่อหน้า

บรรทัดว่าง 1 บรรทัด ซึ่งก็คือบรรทัดที่มีเฉพาะเครื่องหมายดอกจันนำหน้าซึ่งจัดแนว (*) จะปรากฏระหว่างย่อหน้า และก่อนกลุ่มแท็กระดับบล็อกหากมี

บล็อกแท็ก

แท็กบล็อกมาตรฐานที่ใช้จะปรากฏตามลำดับ @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 จะไม่อยู่ในเมธอดที่ลบล้างเมธอดของประเภทซูเปอร์ไทป์เสมอไป