3.6 代碼注釋
正如 Java 和 JavaScript,Kotlin 支持行注釋及塊注釋。
// 這是一個行注釋
/* 這是一個多行的
塊注釋。 */
與 Java 不同的是,Kotlin 的塊注釋可以嵌套。就是說,你可以這樣注釋:
/**
* hhhh
* /**
* fff
* /**
* ggggg
* */
* */
*
* abc
*
*/
fun main(args:Array<String>){
val f = Functions()
println(f.fvoid1())
println(f.fvoid2())
println(f.sum1(1,1))
println(f.sum2(1,1))
}
3.7 語法與標識符
我們知道,任何一門編程語言都會有一些自己專用的關鍵字、符號以及規定的語法規則等等。程序員們使用這些基礎詞匯和語法規則來表達算法步驟,也就是寫代碼的過程。
詞法分析是編譯器對源碼進行編譯的基礎步驟之一。詞法分析是將源程序讀入的字符序列,按照一定的規則轉換成詞法單元(Token)序列的過程。詞法單元是語言中具有獨立意義的最小單元,包括修飾符、關鍵字、常數、運算符、邊界符等等。
3.7.1 修飾符
在Kotlin源碼工程中的kotlin/grammar/src/modifiers.grm文件中,描述了Kotlin語言的修飾符,我們在此作簡要注釋說明:
/**
## Modifiers
*/
modifiers
: (modifier | annotations)*
;
typeModifiers
: (suspendModifier | annotations)*
;
modifier
: classModifier
: accessModifier
: varianceAnnotation
: memberModifier
: parameterModifier
: typeParameterModifier
: functionModifier
: propertyModifier
;
classModifier 類修飾符
: "abstract" 抽象類
: "final" 不可被繼承final類
: "enum" 枚舉類
: "open" 可繼承open類
: "annotation" 注解類
: "sealed" 密封類
: "data" 數據類
;
memberModifier
: "override" 重寫函數
: "open" 可被重寫
: "final" 不可被重寫
: "abstract" 抽象函數
: "lateinit" 后期初始化
;
accessModifier 訪問權限控制, 默認是public
: "private"
: "protected"
: "public"
: "internal" 整個模塊內(模塊(module)是指一起編譯的一組 Kotlin 源代碼文件: 例如,一個 IntelliJ IDEA 模塊,一個 Maven 工程, 或 Gradle 工程,通過 Ant 任務的一次調用編譯的一組文件等)可訪問
;
varianceAnnotation 泛型可變性
: "in"
: "out"
;
parameterModifier
: "noinline"
: "crossinline"
: "vararg" 變長參數
;
typeParameterModifier
: "reified"
;
functionModifier
: "tailrec" 尾遞歸
: "operator"
: "infix"
: "inline"
: "external"
: suspendModifier
;
propertyModifier
: "const"
;
suspendModifier
: "suspend"
;
這些修飾符的完整定義,在kotlin/compiler/frontend/src/org/jetbrains/kotlin/lexer/KtTokens.java源碼中:
KtModifierKeywordToken[] MODIFIER_KEYWORDS_ARRAY =
new KtModifierKeywordToken[] {
ABSTRACT_KEYWORD, ENUM_KEYWORD, OPEN_KEYWORD, INNER_KEYWORD, OVERRIDE_KEYWORD, PRIVATE_KEYWORD,
PUBLIC_KEYWORD, INTERNAL_KEYWORD, PROTECTED_KEYWORD, OUT_KEYWORD, IN_KEYWORD, FINAL_KEYWORD, VARARG_KEYWORD,
REIFIED_KEYWORD, COMPANION_KEYWORD, SEALED_KEYWORD, LATEINIT_KEYWORD,
DATA_KEYWORD, INLINE_KEYWORD, NOINLINE_KEYWORD, TAILREC_KEYWORD, EXTERNAL_KEYWORD, ANNOTATION_KEYWORD, CROSSINLINE_KEYWORD,
CONST_KEYWORD, OPERATOR_KEYWORD, INFIX_KEYWORD, SUSPEND_KEYWORD, HEADER_KEYWORD, IMPL_KEYWORD
};
TokenSet MODIFIER_KEYWORDS = TokenSet.create(MODIFIER_KEYWORDS_ARRAY);
TokenSet TYPE_MODIFIER_KEYWORDS = TokenSet.create(SUSPEND_KEYWORD);
TokenSet TYPE_ARGUMENT_MODIFIER_KEYWORDS = TokenSet.create(IN_KEYWORD, OUT_KEYWORD);
TokenSet RESERVED_VALUE_PARAMETER_MODIFIER_KEYWORDS = TokenSet.create(OUT_KEYWORD, VARARG_KEYWORD);
TokenSet VISIBILITY_MODIFIERS = TokenSet.create(PRIVATE_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD, PROTECTED_KEYWORD);
3.7.2 關鍵字(保留字)
TokenSet KEYWORDS = TokenSet.create(PACKAGE_KEYWORD, AS_KEYWORD, TYPE_ALIAS_KEYWORD, CLASS_KEYWORD, INTERFACE_KEYWORD,
THIS_KEYWORD, SUPER_KEYWORD, VAL_KEYWORD, VAR_KEYWORD, FUN_KEYWORD, FOR_KEYWORD,
NULL_KEYWORD,
TRUE_KEYWORD, FALSE_KEYWORD, IS_KEYWORD,
IN_KEYWORD, THROW_KEYWORD, RETURN_KEYWORD, BREAK_KEYWORD, CONTINUE_KEYWORD, OBJECT_KEYWORD, IF_KEYWORD,
ELSE_KEYWORD, WHILE_KEYWORD, DO_KEYWORD, TRY_KEYWORD, WHEN_KEYWORD,
NOT_IN, NOT_IS, AS_SAFE,
TYPEOF_KEYWORD
);
TokenSet SOFT_KEYWORDS = TokenSet.create(FILE_KEYWORD, IMPORT_KEYWORD, WHERE_KEYWORD, BY_KEYWORD, GET_KEYWORD,
SET_KEYWORD, ABSTRACT_KEYWORD, ENUM_KEYWORD, OPEN_KEYWORD, INNER_KEYWORD,
OVERRIDE_KEYWORD, PRIVATE_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD, PROTECTED_KEYWORD,
CATCH_KEYWORD, FINALLY_KEYWORD, OUT_KEYWORD, FINAL_KEYWORD, VARARG_KEYWORD, REIFIED_KEYWORD,
DYNAMIC_KEYWORD, COMPANION_KEYWORD, CONSTRUCTOR_KEYWORD, INIT_KEYWORD, SEALED_KEYWORD,
FIELD_KEYWORD, PROPERTY_KEYWORD, RECEIVER_KEYWORD, PARAM_KEYWORD, SETPARAM_KEYWORD,
DELEGATE_KEYWORD,
LATEINIT_KEYWORD,
DATA_KEYWORD, INLINE_KEYWORD, NOINLINE_KEYWORD, TAILREC_KEYWORD, EXTERNAL_KEYWORD,
ANNOTATION_KEYWORD, CROSSINLINE_KEYWORD, CONST_KEYWORD, OPERATOR_KEYWORD, INFIX_KEYWORD,
SUSPEND_KEYWORD, HEADER_KEYWORD, IMPL_KEYWORD
);
其中,對應的關鍵字如下:
KtKeywordToken PACKAGE_KEYWORD = KtKeywordToken.keyword("package");
KtKeywordToken AS_KEYWORD = KtKeywordToken.keyword("as");
KtKeywordToken TYPE_ALIAS_KEYWORD = KtKeywordToken.keyword("typealias");
KtKeywordToken CLASS_KEYWORD = KtKeywordToken.keyword("class");
KtKeywordToken THIS_KEYWORD = KtKeywordToken.keyword("this");
KtKeywordToken SUPER_KEYWORD = KtKeywordToken.keyword("super");
KtKeywordToken VAL_KEYWORD = KtKeywordToken.keyword("val");
KtKeywordToken VAR_KEYWORD = KtKeywordToken.keyword("var");
KtKeywordToken FUN_KEYWORD = KtKeywordToken.keyword("fun");
KtKeywordToken FOR_KEYWORD = KtKeywordToken.keyword("for");
KtKeywordToken NULL_KEYWORD = KtKeywordToken.keyword("null");
KtKeywordToken TRUE_KEYWORD = KtKeywordToken.keyword("true");
KtKeywordToken FALSE_KEYWORD = KtKeywordToken.keyword("false");
KtKeywordToken IS_KEYWORD = KtKeywordToken.keyword("is");
KtModifierKeywordToken IN_KEYWORD = KtModifierKeywordToken.keywordModifier("in");
KtKeywordToken THROW_KEYWORD = KtKeywordToken.keyword("throw");
KtKeywordToken RETURN_KEYWORD = KtKeywordToken.keyword("return");
KtKeywordToken BREAK_KEYWORD = KtKeywordToken.keyword("break");
KtKeywordToken CONTINUE_KEYWORD = KtKeywordToken.keyword("continue");
KtKeywordToken OBJECT_KEYWORD = KtKeywordToken.keyword("object");
KtKeywordToken IF_KEYWORD = KtKeywordToken.keyword("if");
KtKeywordToken TRY_KEYWORD = KtKeywordToken.keyword("try");
KtKeywordToken ELSE_KEYWORD = KtKeywordToken.keyword("else");
KtKeywordToken WHILE_KEYWORD = KtKeywordToken.keyword("while");
KtKeywordToken DO_KEYWORD = KtKeywordToken.keyword("do");
KtKeywordToken WHEN_KEYWORD = KtKeywordToken.keyword("when");
KtKeywordToken INTERFACE_KEYWORD = KtKeywordToken.keyword("interface");
// Reserved for future use:
KtKeywordToken TYPEOF_KEYWORD = KtKeywordToken.keyword("typeof");
...
KtKeywordToken FILE_KEYWORD = KtKeywordToken.softKeyword("file");
KtKeywordToken FIELD_KEYWORD = KtKeywordToken.softKeyword("field");
KtKeywordToken PROPERTY_KEYWORD = KtKeywordToken.softKeyword("property");
KtKeywordToken RECEIVER_KEYWORD = KtKeywordToken.softKeyword("receiver");
KtKeywordToken PARAM_KEYWORD = KtKeywordToken.softKeyword("param");
KtKeywordToken SETPARAM_KEYWORD = KtKeywordToken.softKeyword("setparam");
KtKeywordToken DELEGATE_KEYWORD = KtKeywordToken.softKeyword("delegate");
KtKeywordToken IMPORT_KEYWORD = KtKeywordToken.softKeyword("import");
KtKeywordToken WHERE_KEYWORD = KtKeywordToken.softKeyword("where");
KtKeywordToken BY_KEYWORD = KtKeywordToken.softKeyword("by");
KtKeywordToken GET_KEYWORD = KtKeywordToken.softKeyword("get");
KtKeywordToken SET_KEYWORD = KtKeywordToken.softKeyword("set");
KtKeywordToken CONSTRUCTOR_KEYWORD = KtKeywordToken.softKeyword("constructor");
KtKeywordToken INIT_KEYWORD = KtKeywordToken.softKeyword("init");
KtModifierKeywordToken ABSTRACT_KEYWORD = KtModifierKeywordToken.softKeywordModifier("abstract");
KtModifierKeywordToken ENUM_KEYWORD = KtModifierKeywordToken.softKeywordModifier("enum");
KtModifierKeywordToken OPEN_KEYWORD = KtModifierKeywordToken.softKeywordModifier("open");
KtModifierKeywordToken INNER_KEYWORD = KtModifierKeywordToken.softKeywordModifier("inner");
KtModifierKeywordToken OVERRIDE_KEYWORD = KtModifierKeywordToken.softKeywordModifier("override");
KtModifierKeywordToken PRIVATE_KEYWORD = KtModifierKeywordToken.softKeywordModifier("private");
KtModifierKeywordToken PUBLIC_KEYWORD = KtModifierKeywordToken.softKeywordModifier("public");
KtModifierKeywordToken INTERNAL_KEYWORD = KtModifierKeywordToken.softKeywordModifier("internal");
KtModifierKeywordToken PROTECTED_KEYWORD = KtModifierKeywordToken.softKeywordModifier("protected");
KtKeywordToken CATCH_KEYWORD = KtKeywordToken.softKeyword("catch");
KtModifierKeywordToken OUT_KEYWORD = KtModifierKeywordToken.softKeywordModifier("out");
KtModifierKeywordToken VARARG_KEYWORD = KtModifierKeywordToken.softKeywordModifier("vararg");
KtModifierKeywordToken REIFIED_KEYWORD = KtModifierKeywordToken.softKeywordModifier("reified");
KtKeywordToken DYNAMIC_KEYWORD = KtKeywordToken.softKeyword("dynamic");
KtModifierKeywordToken COMPANION_KEYWORD = KtModifierKeywordToken.softKeywordModifier("companion");
KtModifierKeywordToken SEALED_KEYWORD = KtModifierKeywordToken.softKeywordModifier("sealed");
KtModifierKeywordToken DEFAULT_VISIBILITY_KEYWORD = PUBLIC_KEYWORD;
KtKeywordToken FINALLY_KEYWORD = KtKeywordToken.softKeyword("finally");
KtModifierKeywordToken FINAL_KEYWORD = KtModifierKeywordToken.softKeywordModifier("final");
KtModifierKeywordToken LATEINIT_KEYWORD = KtModifierKeywordToken.softKeywordModifier("lateinit");
KtModifierKeywordToken DATA_KEYWORD = KtModifierKeywordToken.softKeywordModifier("data");
KtModifierKeywordToken INLINE_KEYWORD = KtModifierKeywordToken.softKeywordModifier("inline");
KtModifierKeywordToken NOINLINE_KEYWORD = KtModifierKeywordToken.softKeywordModifier("noinline");
KtModifierKeywordToken TAILREC_KEYWORD = KtModifierKeywordToken.softKeywordModifier("tailrec");
KtModifierKeywordToken EXTERNAL_KEYWORD = KtModifierKeywordToken.softKeywordModifier("external");
KtModifierKeywordToken ANNOTATION_KEYWORD = KtModifierKeywordToken.softKeywordModifier("annotation");
KtModifierKeywordToken CROSSINLINE_KEYWORD = KtModifierKeywordToken.softKeywordModifier("crossinline");
KtModifierKeywordToken OPERATOR_KEYWORD = KtModifierKeywordToken.softKeywordModifier("operator");
KtModifierKeywordToken INFIX_KEYWORD = KtModifierKeywordToken.softKeywordModifier("infix");
KtModifierKeywordToken CONST_KEYWORD = KtModifierKeywordToken.softKeywordModifier("const");
KtModifierKeywordToken SUSPEND_KEYWORD = KtModifierKeywordToken.softKeywordModifier("suspend");
KtModifierKeywordToken HEADER_KEYWORD = KtModifierKeywordToken.softKeywordModifier("header");
KtModifierKeywordToken IMPL_KEYWORD = KtModifierKeywordToken.softKeywordModifier("impl");
this
關鍵字
this
關鍵字持有當前對象的引用。我們可以使用this
來引用變量或者成員函數,也可以使用return this
,來返回某個類的引用。
代碼示例
class ThisDemo {
val thisis = "THIS IS"
fun whatIsThis(): ThisDemo {
println(this.thisis) //引用變量
this.howIsThis()// 引用成員函數
return this // 返回此類的引用
}
fun howIsThis(){
println("HOW IS THIS ?")
}
}
測試代碼
@Test
fun testThisDemo(){
val demo = ThisDemo()
println(demo.whatIsThis())
}
輸出
THIS IS
HOW IS THIS ?
com.easy.kotlin.ThisDemo@475232fc
在類的成員中,this 指向的是該類的當前對象。
在擴展函數或者帶接收者的函數字面值中, this 表示在點左側傳遞的 接收者參數。
代碼示例:
>>> val sum = fun Int.(x:Int):Int = this + x
>>> sum
kotlin.Int.(kotlin.Int) -> kotlin.Int
>>> 1.sum(1)
2
>>> val concat = fun String.(x:Any) = this + x
>>> "abc".concat(123)
abc123
>>> "abc".concat(true)
abctrue
如果 this 沒有限定符,它指的是最內層的包含它的作用域。如果我們想要引用其他作用域中的 this,可以使用 this@label 標簽。
代碼示例:
class Outer {
val oh = "Oh!"
inner class Inner {
fun m() {
val outer = this@Outer
val inner = this@Inner
val pthis = this
println("outer=" + outer)
println("inner=" + inner)
println("pthis=" + pthis)
println(this@Outer.oh)
val fun1 = hello@ fun String.() {
val d1 = this // fun1 的接收者
println("d1" + d1)
}
val fun2 = { s: String ->
val d2 = this
println("d2=" + d2)
}
"abc".fun1()
fun2
}
}
}
測試代碼:
@Test
fun testThisKeyWord() {
val outer = Outer()
outer.Inner().m()
}
輸出
outer=com.easy.kotlin.Outer@5114e183
inner=com.easy.kotlin.Outer$Inner@5aa8ac7f
pthis=com.easy.kotlin.Outer$Inner@5aa8ac7f
Oh!
d1abc
super 關鍵字
super關鍵字持有指向其父類的引用。
代碼示例:
open class Father {
open val firstName = "Chen"
open val lastName = "Jason"
fun ff() {
println("FFF")
}
}
class Son : Father {
override var firstName = super.firstName
override var lastName = "Jack"
constructor(lastName: String) {
this.lastName = lastName
}
fun love() {
super.ff() // 調用父類方法
println(super.firstName + " " + super.lastName + " Love " + this.firstName + " " + this.lastName)
}
}
測試代碼
@Test
fun testSuperKeyWord() {
val son = Son("Harry")
son.love()
}
輸出
FFF
Chen Jason Love Chen Harry
3.7.3 操作符和操作符的重載
Kotlin 允許我們為自己的類型提供預定義的一組操作符的實現。這些操作符具有固定的符號表示(如 +
或 *
)和固定的優先級。這些操作符的符號定義如下:
KtSingleValueToken LBRACKET = new KtSingleValueToken("LBRACKET", "[");
KtSingleValueToken RBRACKET = new KtSingleValueToken("RBRACKET", "]");
KtSingleValueToken LBRACE = new KtSingleValueToken("LBRACE", "{");
KtSingleValueToken RBRACE = new KtSingleValueToken("RBRACE", "}");
KtSingleValueToken LPAR = new KtSingleValueToken("LPAR", "(");
KtSingleValueToken RPAR = new KtSingleValueToken("RPAR", ")");
KtSingleValueToken DOT = new KtSingleValueToken("DOT", ".");
KtSingleValueToken PLUSPLUS = new KtSingleValueToken("PLUSPLUS", "++");
KtSingleValueToken MINUSMINUS = new KtSingleValueToken("MINUSMINUS", "--");
KtSingleValueToken MUL = new KtSingleValueToken("MUL", "*");
KtSingleValueToken PLUS = new KtSingleValueToken("PLUS", "+");
KtSingleValueToken MINUS = new KtSingleValueToken("MINUS", "-");
KtSingleValueToken EXCL = new KtSingleValueToken("EXCL", "!");
KtSingleValueToken DIV = new KtSingleValueToken("DIV", "/");
KtSingleValueToken PERC = new KtSingleValueToken("PERC", "%");
KtSingleValueToken LT = new KtSingleValueToken("LT", "<");
KtSingleValueToken GT = new KtSingleValueToken("GT", ">");
KtSingleValueToken LTEQ = new KtSingleValueToken("LTEQ", "<=");
KtSingleValueToken GTEQ = new KtSingleValueToken("GTEQ", ">=");
KtSingleValueToken EQEQEQ = new KtSingleValueToken("EQEQEQ", "===");
KtSingleValueToken ARROW = new KtSingleValueToken("ARROW", "->");
KtSingleValueToken DOUBLE_ARROW = new KtSingleValueToken("DOUBLE_ARROW", "=>");
KtSingleValueToken EXCLEQEQEQ = new KtSingleValueToken("EXCLEQEQEQ", "!==");
KtSingleValueToken EQEQ = new KtSingleValueToken("EQEQ", "==");
KtSingleValueToken EXCLEQ = new KtSingleValueToken("EXCLEQ", "!=");
KtSingleValueToken EXCLEXCL = new KtSingleValueToken("EXCLEXCL", "!!");
KtSingleValueToken ANDAND = new KtSingleValueToken("ANDAND", "&&");
KtSingleValueToken OROR = new KtSingleValueToken("OROR", "||");
KtSingleValueToken SAFE_ACCESS = new KtSingleValueToken("SAFE_ACCESS", "?.");
KtSingleValueToken ELVIS = new KtSingleValueToken("ELVIS", "?:");
KtSingleValueToken QUEST = new KtSingleValueToken("QUEST", "?");
KtSingleValueToken COLONCOLON = new KtSingleValueToken("COLONCOLON", "::");
KtSingleValueToken COLON = new KtSingleValueToken("COLON", ":");
KtSingleValueToken SEMICOLON = new KtSingleValueToken("SEMICOLON", ";");
KtSingleValueToken DOUBLE_SEMICOLON = new KtSingleValueToken("DOUBLE_SEMICOLON", ";;");
KtSingleValueToken RANGE = new KtSingleValueToken("RANGE", "..");
KtSingleValueToken EQ = new KtSingleValueToken("EQ", "=");
KtSingleValueToken MULTEQ = new KtSingleValueToken("MULTEQ", "*=");
KtSingleValueToken DIVEQ = new KtSingleValueToken("DIVEQ", "/=");
KtSingleValueToken PERCEQ = new KtSingleValueToken("PERCEQ", "%=");
KtSingleValueToken PLUSEQ = new KtSingleValueToken("PLUSEQ", "+=");
KtSingleValueToken MINUSEQ = new KtSingleValueToken("MINUSEQ", "-=");
KtKeywordToken NOT_IN = KtKeywordToken.keyword("NOT_IN", "!in");
KtKeywordToken NOT_IS = KtKeywordToken.keyword("NOT_IS", "!is");
KtSingleValueToken HASH = new KtSingleValueToken("HASH", "#");
KtSingleValueToken AT = new KtSingleValueToken("AT", "@");
KtSingleValueToken COMMA = new KtSingleValueToken("COMMA", ",");
3.7.4 操作符優先級(Precedence)
優先級 | 標題 | 符號 |
---|---|---|
最高 | 后綴(Postfix ) |
++ , -- , . , ?. , ?
|
前綴(Prefix) |
- , + , ++ , -- , ! , labelDefinition @
|
|
右手類型運算(Type RHS,right-hand side class type (RHS) ) |
: , as , as?
|
|
乘除取余(Multiplicative) |
* , / , %
|
|
加減(Additive ) |
+ , -
|
|
區間范圍(Range) | .. |
|
Infix函數 | 例如,給Int 定義擴展 infix fun Int.shl(x: Int): Int {...} ,這樣調用 1 shl 2 ,等同于1.shl(2)
|
|
Elvis操作符 | ?: |
|
命名檢查符(Named checks) |
in , !in , is , !is
|
|
比較大小(Comparison) |
< , > , <= , >=
|
|
相等性判斷(Equality) |
== , \!==
|
|
與 (Conjunction) | && |
|
或 (Disjunction) | ll |
|
最低 | 賦值(Assignment) |
= , += , -= , *= , /= , %=
|
注:Markdown表格語法:ll
是||
。
為實現這些的操作符,Kotlin為二元操作符左側的類型和一元操作符的參數類型,提供了相應的函數或擴展函數。
例如在kotlin/core/builtins/native/kotlin/Primitives.kt代碼中,對基本類型Int的操作符的實現代碼如下
public class Int private constructor() : Number(), Comparable<Int> {
...
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public operator fun compareTo(other: Byte): Int
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public operator fun compareTo(other: Short): Int
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public override operator fun compareTo(other: Int): Int
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public operator fun compareTo(other: Long): Int
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public operator fun compareTo(other: Float): Int
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public operator fun compareTo(other: Double): Int
/** Adds the other value to this value. */
public operator fun plus(other: Byte): Int
/** Adds the other value to this value. */
public operator fun plus(other: Short): Int
/** Adds the other value to this value. */
public operator fun plus(other: Int): Int
/** Adds the other value to this value. */
public operator fun plus(other: Long): Long
/** Adds the other value to this value. */
public operator fun plus(other: Float): Float
/** Adds the other value to this value. */
public operator fun plus(other: Double): Double
/** Subtracts the other value from this value. */
public operator fun minus(other: Byte): Int
/** Subtracts the other value from this value. */
public operator fun minus(other: Short): Int
/** Subtracts the other value from this value. */
public operator fun minus(other: Int): Int
/** Subtracts the other value from this value. */
public operator fun minus(other: Long): Long
/** Subtracts the other value from this value. */
public operator fun minus(other: Float): Float
/** Subtracts the other value from this value. */
public operator fun minus(other: Double): Double
/** Multiplies this value by the other value. */
public operator fun times(other: Byte): Int
/** Multiplies this value by the other value. */
public operator fun times(other: Short): Int
/** Multiplies this value by the other value. */
public operator fun times(other: Int): Int
/** Multiplies this value by the other value. */
public operator fun times(other: Long): Long
/** Multiplies this value by the other value. */
public operator fun times(other: Float): Float
/** Multiplies this value by the other value. */
public operator fun times(other: Double): Double
/** Divides this value by the other value. */
public operator fun div(other: Byte): Int
/** Divides this value by the other value. */
public operator fun div(other: Short): Int
/** Divides this value by the other value. */
public operator fun div(other: Int): Int
/** Divides this value by the other value. */
public operator fun div(other: Long): Long
/** Divides this value by the other value. */
public operator fun div(other: Float): Float
/** Divides this value by the other value. */
public operator fun div(other: Double): Double
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public operator fun mod(other: Byte): Int
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public operator fun mod(other: Short): Int
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public operator fun mod(other: Int): Int
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public operator fun mod(other: Long): Long
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public operator fun mod(other: Float): Float
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public operator fun mod(other: Double): Double
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public operator fun rem(other: Byte): Int
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public operator fun rem(other: Short): Int
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public operator fun rem(other: Int): Int
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public operator fun rem(other: Long): Long
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public operator fun rem(other: Float): Float
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public operator fun rem(other: Double): Double
/** Increments this value. */
public operator fun inc(): Int
/** Decrements this value. */
public operator fun dec(): Int
/** Returns this value. */
public operator fun unaryPlus(): Int
/** Returns the negative of this value. */
public operator fun unaryMinus(): Int
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Byte): IntRange
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Short): IntRange
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Int): IntRange
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Long): LongRange
/** Shifts this value left by [bits]. */
public infix fun shl(bitCount: Int): Int
/** Shifts this value right by [bits], filling the leftmost bits with copies of the sign bit. */
public infix fun shr(bitCount: Int): Int
/** Shifts this value right by [bits], filling the leftmost bits with zeros. */
public infix fun ushr(bitCount: Int): Int
/** Performs a bitwise AND operation between the two values. */
public infix fun and(other: Int): Int
/** Performs a bitwise OR operation between the two values. */
public infix fun or(other: Int): Int
/** Performs a bitwise XOR operation between the two values. */
public infix fun xor(other: Int): Int
/** Inverts the bits in this value. */
public fun inv(): Int
public override fun toByte(): Byte
public override fun toChar(): Char
public override fun toShort(): Short
public override fun toInt(): Int
public override fun toLong(): Long
public override fun toFloat(): Float
public override fun toDouble(): Double
}
從源代碼我們可以看出,重載操作符的函數需要用 operator
修飾符標記。中綴操作符的函數使用infix
修飾符標記。
3.7.5 一元操作符(unary operation)
前綴操作符
表達式 | 翻譯為 |
---|---|
+a |
a.unaryPlus() |
-a |
a.unaryMinus() |
!a |
a.not() |
例如,當編譯器處理表達式 +a
時,它將執行以下步驟:
- 確定
a
的類型,令其為T
。 - 為接收者
T
查找一個帶有operator
修飾符的無參函數unaryPlus()
,即成員函數或擴展函數。 - 如果函數不存在或不明確,則導致編譯錯誤。
- 如果函數存在且其返回類型為
R
,那就表達式+a
具有類型R
。
編譯器對這些操作以及所有其他操作都針對基本類型做了優化,不會引入函數調用的開銷。
以下是如何重載一元減運算符的示例:
package com.easy.kotlin
/**
* Created by jack on 2017/6/10.
*/
class OperatorDemo {
}
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus() = Point(-x, -y)
測試代碼:
package com.easy.kotlin
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
/**
* Created by jack on 2017/6/10.
*/
@RunWith(JUnit4::class)
class OperatorDemoTest {
@Test
fun testPointUnaryMinus() {
val p = Point(1, 1)
val np = -p
println(np) //Point(x=-1, y=-1)
}
}
遞增和遞減
表達式 | 翻譯為 |
---|---|
a++ |
a.inc() 返回值是a
|
a-- |
a.dec() 返回值是a
|
++a |
a.inc() 返回值是a+1
|
--a |
a.dec() 返回值是a-1
|
inc()
和 dec()
函數必須返回一個值,它用于賦值給使用 ++
或 --
操作的變量。
編譯器執行以下步驟來解析后綴形式的操作符,例如 a++
:
- 確定
a
的類型,令其為T
。 - 查找一個適用于類型為
T
的接收者的、帶有operator
修飾符的無參數函數inc()
。 - 檢查函數的返回類型是
T
的子類型。
計算表達式的步驟是:
- 把
a
的初始值存儲到臨時存儲a_
中 - 把
a.inc()
結果賦值給a
- 把
a_
作為表達式的結果返回
( a--
同理分析)。
對于前綴形式 ++a
和 --a
解析步驟類似,但是返回值是取的新值來返回:
- 把
a.inc()
結果賦值給a
- 把
a
的新值a+1
作為表達式結果返回
( --a
同理分析)。
3.7.6 二元操作符
算術運算符
表達式 | 翻譯為 |
---|---|
a + b |
a.plus(b) |
a - b |
a.minus(b) |
a * b |
a.times(b) |
a / b |
a.div(b) |
a % b |
a.rem(b) 、 a.mod(b)
|
a..b |
a.rangeTo(b) |
代碼示例
>>> val a=10
>>> val b=3
>>> a+b
13
>>> a-b
7
>>> a/b
3
>>> a%b
1
>>> a..b
10..3
>>> b..a
3..10
字符串的+
運算符重載
先用代碼舉個例子:
>>> ""+1
1
>>> 1+""
error: none of the following functions can be called with the arguments supplied:
public final operator fun plus(other: Byte): Int defined in kotlin.Int
public final operator fun plus(other: Double): Double defined in kotlin.Int
public final operator fun plus(other: Float): Float defined in kotlin.Int
public final operator fun plus(other: Int): Int defined in kotlin.Int
public final operator fun plus(other: Long): Long defined in kotlin.Int
public final operator fun plus(other: Short): Int defined in kotlin.Int
1+""
^
從上面的示例,我們可以看出,在Kotlin中1+""
是不允許的(這地方,相比Scala,寫這樣的Kotlin代碼就顯得不大友好),只能顯式調用toString
來相加:
>>> 1.toString()+""
1
自定義重載的 +
運算符
下面我們使用一個計數類 Counter 重載的 +
運算符來增加index的計數值。
代碼示例
data class Counter(var index: Int)
operator fun Counter.plus(increment: Int): Counter {
return Counter(index + increment)
}
測試類
package com.easy.kotlin
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
/**
* Created by jack on 2017/6/10.
*/
@RunWith(JUnit4::class)
class OperatorDemoTest
@Test
fun testCounterIndexPlus() {
val c = Counter(1)
val cplus = c + 10
println(cplus) //Counter(index=11)
}
}
in
操作符
表達式 | 翻譯為 |
---|---|
a in b |
b.contains(a) |
a !in b |
!b.contains(a) |
索引訪問操作符
表達式 | 翻譯為 |
---|---|
a[i] |
a.get(i) |
a[i] = b |
a.set(i, b) |
方括號轉換為調用帶有適當數量參數的 get
和 set
。
調用操作符
表達式 | 翻譯為 |
---|---|
a() |
a.invoke() |
a(i) |
a.invoke(i) |
圓括號轉換為調用帶有適當數量參數的 invoke
。
計算并賦值
表達式 | 翻譯為 |
---|---|
a += b |
a.plusAssign(b) |
a -= b |
a.minusAssign(b) |
a *= b |
a.timesAssign(b) |
a /= b |
a.divAssign(b) |
a %= b |
a.modAssign(b) |
對于賦值操作,例如 a += b
,編譯器會試著生成 a = a + b
的代碼(這里包含類型檢查:a + b
的類型必須是 a
的子類型)。
相等與不等操作符
Kotlin 中有兩種類型的相等性:
- 引用相等
===
!==
(兩個引用指向同一對象) - 結構相等
==
!=
( 使用equals()
判斷)
表達式 | 翻譯為 |
---|---|
a == b |
a?.equals(b) ?: (b === null) |
a != b |
!(a?.equals(b) ?: (b === null)) |
這個 ==
操作符有些特殊:它被翻譯成一個復雜的表達式,用于篩選 null
值。
意思是:如果 a 不是 null 則調用 equals(Any?)
函數并返回其值;否則(即 a === null
)就計算 b === null
的值并返回。
當與 null 顯式比較時,a == null
會被自動轉換為 a=== null
注意:===
和 !==
不可重載。
Elvis 操作符 ?:
在Kotin中,Elvis操作符特定是跟null比較。也就是說
y = x?:0
等價于
val y = if(x!==null) x else 0
主要用來作null
安全性檢查。
Elvis操作符 ?:
是一個二元運算符,如果第一個操作數為真,則返回第一個操作數,否則將計算并返回其第二個操作數。它是三元條件運算符的變體。命名靈感來自貓王的發型風格。
Kotlin中沒有這樣的三元運算符 true?1:0
,取而代之的是if(true) 1 else 0
。而Elvis操作符算是精簡版的三元運算符。
我們在Java中使用的三元運算符的語法,你通常要重復變量兩次, 示例:
String name = "Elvis Presley";
String displayName = (name != null) ? name : "Unknown";
取而代之,你可以使用Elvis操作符。
String name = "Elvis Presley";
String displayName = name?:"Unknown"
我們可以看出,用Elvis操作符(?:)可以把帶有默認值的if/else結構寫的及其短小。用Elvis操作符不用檢查null(避免了NullPointerException
),也不用重復變量。
這個Elvis操作符功能在Spring 表達式語言 (SpEL)中提供。
在Kotlin中當然就沒有理由不支持這個特性。
代碼示例:
>>> val x = null
>>> val y = x?:0
>>> y
0
>>> val x = false
>>> val y = x?:0
>>> y
false
>>> val x = ""
>>> val y = x?:0
>>> y
>>> val x = "abc"
>>> val y = x?:0
>>> y
abc
比較操作符
表達式 | 翻譯為 |
---|---|
a > b |
a.compareTo(b) > 0 |
a < b |
a.compareTo(b) < 0 |
a >= b |
a.compareTo(b) >= 0 |
a <= b |
a.compareTo(b) <= 0 |
所有的比較都轉換為對 compareTo
的調用,這個函數需要返回 Int
值
用infix函數自定義中綴操作符
我們可以通過自定義infix函數來實現中綴操作符。
代碼示例
data class Person(val name: String, val age: Int)
infix fun Person.grow(years: Int): Person {
return Person(name, age + years)
}
測試代碼
package com.easy.kotlin
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
/**
* Created by jack on 2017/6/11.
*/
@RunWith(JUnit4::class)
class InfixFunctionDemoTest {
@Test fun testInfixFuntion() {
val person = Person("Jack", 20)
println(person.grow(2))
println(person grow 2)
}
}
輸出
Person(name=Jack, age=22)
Person(name=Jack, age=22)
3.8 函數擴展和屬性擴展(Extensions)
Kotlin 支持 擴展函數 和 擴展屬性。其能夠擴展一個類的新功能而無需繼承該類或使用像裝飾者這樣的設計模式等。
大多數時候我們在頂層定義擴展,即直接在包里:
package com.easy.kotlin
/**
* Created by jack on 2017/6/11.
*/
val <T> List<T>.lastIndex: Int get() = size - 1
fun String.notEmpty(): Boolean {
return !this.isEmpty()
}
這樣我們就可以在整個包里使用這些擴展。
要使用其他包的擴展,我們需要在調用方導入它:
package com.example.usage
import foo.bar.goo // 導入所有名為“goo”的擴展
// 或者
import foo.bar.* // 從“foo.bar”導入一切
fun usage(baz: Baz) {
baz.goo()
}
3.8.1 擴展函數
聲明一個擴展函數,我們需要用被擴展的類型來作為前綴。
比如說,我們不喜歡類似下面的雙重否定式的邏輯判斷(繞腦子):
>>> !"123".isEmpty()
true
我們就可以為String
類型擴展一個notEmpty()
函數:
>>> fun String.notEmpty():Boolean{
... return !this.isEmpty()
... }
>>> "".notEmpty()
false
>>> "123".notEmpty()
true
下面代碼為 MutableList<Int>
添加一個swap
函數:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // this對應該列表
this[index1] = this[index2]
this[index2] = tmp
}
這個 this
關鍵字在擴展函數內部對應到接收者對象(傳過來的在點.
符號前的對象) 現在,我們對任意 MutableList<Int>
調用該函數了。
當然,這個函數對任何 MutableList<T>
起作用,我們可以泛化它:
fun <T> MutableList<T>.mswap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”對應該列表
this[index1] = this[index2]
this[index2] = tmp
}
為了在接收者類型表達式中使用泛型,我們要在函數名前聲明泛型參數。
完整代碼示例
package com.easy.kotlin
/**
- Created by jack on 2017/6/11.
*/
val <T> List<T>.lastIndex: Int get() = size - 1
fun String.notEmpty(): Boolean {
return !this.isEmpty()
}
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // this對應該列表m
this[index1] = this[index2]
this[index2] = tmp
}
fun <T> MutableList<T>.mswap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”對應該列表
this[index1] = this[index2]
this[index2] = tmp
}
class ExtensionsDemo {
fun useExtensions() {
val a = "abc"
println(a.notEmpty())//true
val mList = mutableListOf<Int>(1, 2, 3, 4, 5)
println("Before Swap:")
println(mList)//[1, 2, 3, 4, 5]
mList.swap(0, mList.size - 1)
println("After Swap:")
println(mList)//[5, 2, 3, 4, 1]
val mmList = mutableListOf<String>("a12", "b34", "c56", "d78")
println("Before Swap:")
println(mmList)//[a12, b34, c56, d78]
mmList.mswap(1, 2)
println("After Swap:")
println(mmList)//[a12, c56, b34, d78]
val mmmList = mutableListOf<Int>(100, 200, 300, 400, 500)
println("Before Swap:")
println(mmmList)
mmmList.mswap(0, mmmList.lastIndex)
println("After Swap:")
println(mmmList)
}
class Inner {
fun useExtensions() {
val mmmList = mutableListOf<Int>(100, 200, 300, 400, 500)
println(mmmList.lastIndex)
}
}
}
測試代碼
package com.easy.kotlin
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
/**
- Created by jack on 2017/6/11.
*/
@RunWith(JUnit4::class)
class ExtensionsDemoTest {
@Test fun testExtensionsDemo() {
val demo = ExtensionsDemo()
demo.useExtensions()
}
}
擴展不是真正的修改他們所擴展的類。我們定義一個擴展,其實并沒有在一個類中插入新函數,僅僅是通過該類型的變量,用點.表達式去調用這個新函數。
3.8.2 擴展屬性
和函數類似,Kotlin 支持擴展屬性:
val <T> List<T>.lastIndex: Int
get() = size - 1
注意:由于擴展沒有實際的將成員插入類中,因此對擴展的屬性來說,它的行為只能由顯式提供的 getters/setters 定義。
代碼示例:
package com.easy.kotlin
/**
* Created by jack on 2017/6/11.
*/
val <T> List<T>.lastIndex: Int get() = size - 1
我們可以直接使用包com.easy.kotlin
中擴展的屬性lastIndex
:
3.9 空指針安全(Null-safety)
我們寫代碼的時候知道,在Java中NPE(NullPointerExceptions)是一件成程序員幾近崩潰的事情。很多時候,雖然費盡體力腦力,仍然防不勝防。
以前,當我們不確定一個DTO類中的字段是否已初始化時,可以使用@Nullable和@NotNull注解來聲明,但功能很有限。
現在好了,Kotlin在編譯器級別,把你之前在Java中需要寫的null check代碼完成了。
但是,當我們的代碼
- 顯式調用
throw NullPointerException()
- 使用了
!!
操作符 - 調用的外部 Java 代碼有NPE
- 對于初始化,有一些數據不一致(如一個未初始化的
this
用于構造函數的某個地方)
也可能會發生NPE。
在Kotlin中null
等同于空指針。我們來通過代碼來看一下null
的有趣的特性:
首先,一個非空引用不能直接賦值為null
:
>>> var a="abc"
>>> a=null
error: null can not be a value of a non-null type String
a=null
^
>>> var one=1
>>> one=null
error: null can not be a value of a non-null type Int
one=null
^
>>> var arrayInts = intArrayOf(1,2,3)
>>> arrayInts=null
error: null can not be a value of a non-null type IntArray
arrayInts=null
^
這樣,我們就可以放心地調用 a 的方法或者訪問它的屬性,不會導致 NPE:
>>> val a="abc"
>>> a.length
3
如果要允許為空,我們可以在變量的類型后面加個問號?聲明一個變量為可空的:
>>> var a:String?="abc"
>>> a=null
>>> var one:Int?=1
>>> one=null
>>> var arrayInts:IntArray?=intArrayOf(1,2,3)
>>> arrayInts=null
>>> arrayInts
null
如果我們聲明了一個可空String?類型變量na ,然后直接調用length屬性,這將是不安全的。編譯器會直接報錯:
>>> var na:String?="abc"
>>> na=null
>>> na.length
error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
na.length
我們使用安全調用?. 和 非空斷言調用 !!.
>>> na?.length
null
>>> na!!.length
kotlin.KotlinNullPointerException
我們可以看出,代碼返回了null 和 kotlin.KotlinNullPointerException。
安全調用在鏈式調用中很有用。在調用鏈中如果任意一個屬性(環節)為空,這個鏈式調用就會安全返回 null。
如果要只對非空值執行某個操作,安全調用操作符可以與 let (以調用者的值作為參數來執行指定的函數塊,并返回其結果)一起使用:
>>> val listWithNulls: List<String?> = listOf("A", "B",null)
>>> listWithNulls
[A, B, null]
>>> listWithNulls.forEach{
... it?.let{println(it)}
... }
A
B
本章小結
本章我們學習了Kotlin語言的基本詞匯(關鍵字、標識符等)、句子(流程控制、表達式、操作符等)和一些基礎語法。同時,學習了空指針安全、擴展函數與擴展屬性等的語言特性。
我們將在下一章節中介紹Kotlin的基本類型和類型系統。