// 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) 是唯一一种可以出现在源文件中任意位置的空白字符。这意味着:
对于任何具有特殊转义序列(\b、\n、\r、\t、'、"、\ 和 $)的字符,将使用该序列,而不是相应的 Unicode 转义字符(例如 \u000a)。
对于其余非 ASCII 字符,要么使用实际的 Unicode 字符(例如 ∞),要么使用等效的 Unicode 转义字符(例如 \u221e)。具体选择仅取决于哪种字符可使代码更容易阅读和理解。建议不要对任何位置的可输出字符使用 Unicode 转义字符,强烈建议不要在字符串字面量和注释之外使用 Unicode 转义字符。
// 最好:即使没有注释,也非常清楚。
val unitAbbrev = "μs"
// 差:没有理由对可打印字符使用转义。
val unitAbbrev = "\u03bcs" // μs
// 差:读者不知道这是什么。
val unitAbbrev = "\u03bcs"
// 好:对不可打印字符使用转义,并在必要时添加注释。
return "\ufeff" + content
/*
* Copyright 2017 Google, Inc.
*
* ...
*/
KDoc 样式
/**
* Copyright 2017 Google, Inc.
*
* ...
*/
单行样式
// Copyright 2017 Google, Inc.
//
// ...
@file:JvmName("Foo")
package org.jetbrains.demo
if (string.isEmpty()) return
val result =
if (string.isEmpty()) DEFAULT_VALUE else string
when (value) {
0 -> return
// …
}
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 (K&R) 样式(“埃及括号”):
object : MyClass() {
override fun foo() {
if (condition()) {
try {
something()
} catch (e: ProblemException) {
recover()
}
} else if (otherCondition()) {
somethingElse()
} else {
lastThing()
}
}
}
try {
doSomething()
} catch (e: Exception) {} // WRONG!
try {
doSomething()
} catch (e: Exception) {
} // Okay
// Okay
val value = if (string.isEmpty()) 0 else 1
// WRONG!
val value = if (string.isEmpty())
0
else
1
// Okay
val value = if (string.isEmpty()) {
0
} else {
1
}
换行的首要原则是:更倾向于在较高的句法级别换行。此外:
某行在运算符或 infix 函数名称处换行时,换行符将在该运算符或 infix 函数名称后面。
某行在以下“类似运算符”的符号处换行时,换行符将在该符号前面:
1)点分隔符(.、?.)。
2)成员引用的两个冒号 ( :: )。
3)方法或构造函数名称始终贴在它后面的左圆括号 (() 上。
4)逗号 (,) 始终贴在它前面的标记上。
5)lambda 箭头 (->) 始终贴在它前面的参数列表上。
换行的主要目标是让代码清晰,而不一定是让代码适合放在最少数量的行中。
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)
var directory: File? = null
set(value) {
// …
}
val defaultExtension: String get() = "kt"
// WRONG!
for(i in 0..1) {
}
// Okay
for (i in 0..1) {
}
// WRONG!
}else {
}
// Okay
} else {
}
// WRONG!
if (list.isEmpty()){
}
// Okay
if (list.isEmpty()) {
}
// WRONG!
val two = 1+1
// Okay
val two = 1 + 1
// WRONG!
ints.map { value->value.toString() }
// Okay
ints.map { value -> value.toString() }
// 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
// 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)
}
enum class Answer { YES, NO, MAYBE }
enum class Answer {
YES,
NO,
MAYBE {
override fun toString() = """¯\_(ツ)_/¯"""
}
}
@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")
// Okay
package com.example.deepspace
// WRONG!
package com.example.deepSpace
// WRONG!
package com.example.deep_space
@Test fun pop_emptyStack() {
// …
}
// WRONG!
fun `test every possible case`() {}
// OK
fun testEveryPossibleCase() {}
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
val variable = "var"
val nonConstScalar = "non-const"
val mutableCollection: MutableSet
private var _table: Map
/**
* Multiple lines of KDoc text are written here,
* wrapped normally…
*/
fun method(arg: String) {
// …
}
/** An especially short bit of KDoc. */
段落
一个空白行(即,仅包含对齐的前导星号 (*) 的行)会出现在段落之间,以及成组的块标记(如果存在)前面。
块标记
使用的任何标准“块标记”都按 @constructor、@receiver、@param、@property、@return、@throws 和 @see 的顺序出现,并且这些块标记从不会说明为空。当块标记不适合放在一行时,连续行会从 @ 的位置开始缩进 4 个空格。
摘要片段
每个 KDoc 块都以一个简短的摘要片段开头。此片段是唯一一个会出现在某些上下文(例如类和方法索引)中的文本部分。这是一个片段(名词短语或动词短语),而不是一个完整的句子。它不以“A Foo
is a…”或“This method returns…”开头,也不必形成一个完整的祈使句(如“Save the record.”)。不过,此片段就像一个完整的句子一样首字母大写并加标点符号。
用法
至少每个 public 类型以及这样的类型的每个 public 或 protected 成员都应存在 KDoc。 例外情况: