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

เอกสารนี้เป็นคำจำกัดความที่สมบูรณ์ของมาตรฐานการเขียนโค้ด Android ของ Google สำหรับซอร์สโค้ดในภาษาการเขียนโปรแกรม Kotlin ไฟล์แหล่งที่มา Kotlin อธิบายว่าอยู่ใน Google Android Style ก็ต่อเมื่อไฟล์ดังกล่าวเป็นไปตามกฎในที่นี้เท่านั้น

เช่นเดียวกับคู่มือสไตล์การจัดโปรแกรมอื่นๆ ปัญหาที่กล่าวถึงไม่ได้ครอบคลุมเพียงปัญหาด้านความสวยงามของการจัดรูปแบบเท่านั้น แต่ยังรวมถึงแบบแผนหรือมาตรฐานการเขียนโค้ดประเภทอื่นๆ ด้วย อย่างไรก็ตาม เอกสารนี้มุ่งเน้นไปที่กฎที่เคร่งครัดและเข้มงวดเป็นหลักซึ่งเราปฏิบัติตามในระดับสากล และหลีกเลี่ยงการให้คำแนะนำที่ไม่สามารถบังคับใช้ได้อย่างชัดเจน (ไม่ว่าจะดำเนินการโดยมนุษย์หรือเครื่องมือ)

อัปเดตล่าสุด: 06-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) เป็นอักขระช่องว่างเพียงตัวเดียวที่ปรากฏในที่ใดก็ได้ในไฟล์ต้นฉบับ ซึ่งหมายความว่า

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

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

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

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

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

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

โครงสร้าง

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

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

จะมีบรรทัดว่าง 1 บรรทัดคั่นแต่ละส่วน

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

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

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

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

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

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

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

คำสั่งแพ็กเกจไม่ได้อยู่ภายใต้ขีดจำกัดของคอลัมน์ใดๆ และไม่มีการขึ้นบรรทัดใหม่

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

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

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

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

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

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

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

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

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

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

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

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

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

เหล็กดัดฟัน

ไม่จำเป็นต้องใส่วงเล็บปีกกาสำหรับ Branch ของ when และนิพจน์ if ที่มี Branch ของ 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)
}

บล็อกที่ไม่ว่างเปล่า

วงเล็บปีกกาจะใช้รูปแบบเคอร์นิแกนและริตชี ("วงเล็บปีกกา") สำหรับ บล็อกว่างและโครงสร้างที่เหมือนบล็อก:

  • ไม่มีการขึ้นบรรทัดใหม่ก่อนวงเล็บเปิด
  • ขึ้นบรรทัดใหม่หลังวงเล็บเปิด
  • ขึ้นบรรทัดใหม่ก่อนวงเล็บปิด
  • ตัวแบ่งบรรทัดหลังวงเล็บปิด เฉพาะถ้าวงเล็บนั้นสิ้นสุด หรือสิ้นสุดส่วนเนื้อหาของฟังก์ชัน ตัวสร้าง หรือคลาสที่มีชื่อ เช่น ไม่มีตัวแบ่งบรรทัดหลังวงเล็บปีกกาหากตามด้วยวงเล็บ 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 ช่องว่าง เมื่อสิ้นสุดการบล็อก การเยื้องจะกลับไปที่ระดับการเยื้องก่อนหน้า ระดับการเยื้องจะใช้กับทั้งโค้ดและความคิดเห็นตลอดทั้งบล็อก

หนึ่งข้อความต่อบรรทัด

ตามด้วยการขึ้นบรรทัดใหม่แต่ละข้อความ ไม่ใช้อัฒภาค

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

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

ข้อยกเว้น:

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

จุดที่แบ่งได้

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

  • เมื่อมีบรรทัดแยกที่โอเปอเรเตอร์หรือชื่อฟังก์ชัน 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"

เว้นวรรค

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

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

  • ระหว่างสมาชิกต่อเนื่องของชั้นเรียน ได้แก่ พร็อพเพอร์ตี้ เครื่องมือสร้าง ฟังก์ชัน คลาสที่ซ้อนกัน ฯลฯ
    • ข้อยกเว้น: บรรทัดว่างระหว่าง 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
    
    นอกจากนี้ยังรวมถึงสัญลักษณ์ "คล้ายโอเปอเรเตอร์" ต่อไปนี้ด้วย
    • ลูกศรในนิพจน์แลมบ์ดา (->) วันที่
      // 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 แยกบรรทัดกัน ก็ไม่จำเป็นต้องมีบรรทัดว่างระหว่างค่าดังกล่าว ยกเว้นในกรณีที่ค่านั้นกำหนดเป็น Body

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

เมื่อแสดงคำอธิบายประกอบเพียง 1 รายการโดยไม่มีอาร์กิวเมนต์ อาจวางอยู่บนบรรทัดเดียวกับการประกาศก็ได้

@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

ชื่อฟังก์ชัน

ชื่อฟังก์ชันจะเขียนด้วยอูฐและมักจะเป็นคำกริยาหรือวลีกริยา เช่น 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 ตัวปรับแต่ง

ชื่อที่ไม่คงที่

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

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

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

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

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

เคส Camel

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

ขึ้นต้นด้วยรูปแบบร้อยแก้วของชื่อ:

  1. แปลงวลีเป็น ASCII แบบธรรมดาและลบเครื่องหมายอะพอสทรอฟีออก เช่น "อัลกอริทึมของ Müller" อาจกลายเป็น "อัลกอริทึมของ Mullers"
  2. ให้หารผลลัพธ์นี้เป็นคำ โดยเว้นวรรคและใช้เครื่องหมายวรรคตอนที่เหลือ (โดยทั่วไปคือขีดกลาง) แนะนำ: หากคำใดมีลักษณะคล้ายตัวอูฐทั่วไปอยู่แล้วตามการใช้งานทั่วไป ให้แยกคำนี้ออกเป็นส่วนๆ (เช่น “AdWords” กลายเป็น “คำโฆษณา”) โปรดทราบว่าคำเช่น “iOS” นั้นไม่ใช่คำที่ใช้ตัวพิมพ์เล็ก/ใหญ่แบบอูฐ ขัดต่อกฎเกณฑ์ใดๆ ดังนั้นคำแนะนำนี้จึงไม่มีผลบังคับใช้
  3. จากนั้นพิมพ์ตัวพิมพ์เล็กทุกอย่าง (รวมถึงตัวย่อ) จากนั้นดำเนินการอย่างใดอย่างหนึ่งต่อไปนี้
    • ทำให้อักขระตัวแรกของแต่ละคำเป็นตัวพิมพ์ใหญ่เป็นตัวพิมพ์ใหญ่
    • ทำให้อักขระตัวแรกของแต่ละคำเป็นตัวพิมพ์ใหญ่ ยกเว้นอักษรตัวแรกเป็นตัวพิมพ์ใหญ่ แบบอูฐ
  4. สุดท้าย รวมทุกคำไว้ในตัวระบุเดียว

โปรดทราบว่าไม่คำนึงถึงตัวพิมพ์เล็กหรือใหญ่ของคำต้นฉบับเกือบทั้งหมด

แบบฟอร์มร้อยแก้ว ถูกต้อง ไม่ถูกต้อง
"คำขอ HTTP สำหรับ XML" 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 ในกรณีที่ไม่มีสิ่งอื่นใดที่คุ้มค่าที่จะพูดจริงๆ แต่ "คืนอาหาร"

การอ้างอิงข้อยกเว้นนี้เป็นสิ่งที่ไม่เหมาะสมเพื่อพิสูจน์ให้ละเว้นการกระทำที่เกี่ยวข้อง ที่ผู้อ่านทั่วไปอาจต้องรู้ ตัวอย่างเช่น สำหรับ ฟังก์ชันชื่อ getCanonicalName หรือพร็อพเพอร์ตี้ชื่อ canonicalName ไม่ละเว้นเอกสารประกอบ (พร้อมเหตุผลว่า /** Returns the canonical name. */) ในกรณีที่ผู้อ่านทั่วไปอาจไม่มี ลองคิดดูว่าคำว่า "ชื่อ Canonical" คืออะไร หมายความว่า

ข้อยกเว้น: ลบล้าง

ในบางครั้ง KDoc จะไม่ปรากฏขึ้นในเมธอดที่ลบล้างเมธอดประเภทขั้นสูง