เอกสารนี้เป็นคำจำกัดความที่สมบูรณ์ของมาตรฐานการเขียนโค้ด Android ของ Google สำหรับซอร์สโค้ดในภาษาการเขียนโปรแกรม Kotlin ไฟล์ต้นฉบับ Kotlin จะถือว่าอยู่ในรูปแบบของ Google Android ก็ต่อเมื่อเป็นไปตามกฎที่ระบุไว้ในที่นี้เท่านั้น
เช่นเดียวกับแนวทางการเขียนโปรแกรมอื่นๆ ปัญหาที่ครอบคลุมไม่ได้มีเพียงปัญหาด้านสุนทรียภาพของการจัดรูปแบบเท่านั้น แต่ยังรวมถึงรูปแบบอื่นๆ หรือมาตรฐานการเขียนโค้ดด้วย อย่างไรก็ตาม เอกสารนี้จะเน้นที่กฎที่ชัดเจนซึ่งเราปฏิบัติตามในทุกกรณี และหลีกเลี่ยงการให้คำแนะนำที่บังคับใช้ได้ไม่ชัดเจน (ไม่ว่าจะเป็นโดยมนุษย์หรือเครื่องมือ)
ไฟล์ต้นฉบับ
ไฟล์ต้นฉบับทั้งหมดต้องเข้ารหัสเป็น 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) }
- ลูกศรในนิพจน์ Lambda (
-
ก่อนเครื่องหมายโคลอน (
:) เฉพาะในกรณีที่ใช้ในการประกาศคลาสเพื่อระบุ คลาสพื้นฐานหรืออินเทอร์เฟซ หรือเมื่อใช้ใน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" ใช้รูปแบบต่อไปนี้เพื่อปรับปรุงความสามารถในการคาดการณ์
เริ่มต้นด้วยรูปแบบร้อยแก้วของชื่อ
- แปลงวลีเป็น ASCII แบบธรรมดาและนำเครื่องหมายอัญประกาศออก เช่น "อัลกอริทึมของมึลเลอร์" อาจกลายเป็น "อัลกอริทึมของมึลเลอร์"
- แบ่งผลลัพธ์นี้ออกเป็นคำ โดยแยกตามช่องว่างและเครื่องหมายวรรคตอนที่เหลือ (โดยปกติคือขีดกลาง) แนะนำ: หากคำใดมีรูปแบบ Camel Case ตามธรรมเนียมในการใช้งานทั่วไป ให้แยกคำนั้นออกเป็นส่วนประกอบ (เช่น "AdWords" จะกลายเป็น "ad words") โปรดทราบว่าคำอย่างเช่น "iOS" ไม่ได้อยู่ในรูปแบบ Camel Case โดยแท้จริง แต่เป็นการละเมิดธรรมเนียม ดังนั้นคำแนะนำนี้จึงไม่มีผล
- จากนั้นเปลี่ยนตัวอักษรทั้งหมดเป็นตัวพิมพ์เล็ก (รวมถึงตัวย่อ) แล้วทำอย่างใดอย่างหนึ่งต่อไปนี้
- เปลี่ยนอักขระแรกของแต่ละคำเป็นตัวพิมพ์ใหญ่เพื่อให้ได้รูปแบบ Pascal Case
- เปลี่ยนอักขระแรกของแต่ละคำเป็นตัวพิมพ์ใหญ่ ยกเว้นคำแรก เพื่อให้ได้รูปแบบ Camel Case
- สุดท้าย ให้รวมคำทั้งหมดเป็นตัวระบุเดียว
โปรดทราบว่าระบบจะไม่สนใจการใช้อักษรตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ของคำเดิมเกือบทั้งหมด
| รูปแบบร้อยแก้ว | ถูกต้อง | ไม่ถูกต้อง |
|---|---|---|
| "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 จะไม่อยู่ในเมธอดที่ลบล้างเมธอดของประเภทซูเปอร์ไทป์เสมอไป