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