Kotlin語言基礎(chǔ)(二)

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 語法與標(biāo)識符

我們知道储狭,任何一門編程語言都會有一些自己專用的關(guān)鍵字互婿、符號以及規(guī)定的語法規(guī)則等等。程序員們使用這些基礎(chǔ)詞匯和語法規(guī)則來表達(dá)算法步驟辽狈,也就是寫代碼的過程慈参。

詞法分析是編譯器對源碼進(jìn)行編譯的基礎(chǔ)步驟之一。詞法分析是將源程序讀入的字符序列刮萌,按照一定的規(guī)則轉(zhuǎn)換成詞法單元(Token)序列的過程驮配。詞法單元是語言中具有獨立意義的最小單元,包括修飾符着茸、關(guān)鍵字壮锻、常數(shù)、運算符元扔、邊界符等等躯保。

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" 數(shù)據(jù)類
  ;

memberModifier
  : "override" 重寫函數(shù)
  : "open" 可被重寫
  : "final" 不可被重寫
  : "abstract" 抽象函數(shù)
  : "lateinit" 后期初始化
  ;

accessModifier 訪問權(quán)限控制, 默認(rèn)是public
  : "private"
  : "protected"
  : "public"
  : "internal"  整個模塊內(nèi)(模塊(module)是指一起編譯的一組 Kotlin 源代碼文件: 例如途事,一個 IntelliJ IDEA 模塊验懊,一個 Maven 工程, 或 Gradle 工程,通過 Ant 任務(wù)的一次調(diào)用編譯的一組文件等)可訪問
  ;

varianceAnnotation 泛型可變性
  : "in" 
  : "out"
  ;

parameterModifier
  : "noinline" 
  : "crossinline" 
  : "vararg" 變長參數(shù)
  ;

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 關(guān)鍵字(保留字)

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
    );

其中义图,對應(yīng)的關(guān)鍵字如下:

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 關(guān)鍵字

this關(guān)鍵字持有當(dāng)前對象的引用。我們可以使用this來引用變量或者成員函數(shù)召烂,也可以使用return this碱工,來返回某個類的引用。

代碼示例

class ThisDemo {
    val thisis = "THIS IS"

    fun whatIsThis(): ThisDemo {
        println(this.thisis) //引用變量
        this.howIsThis()// 引用成員函數(shù)
        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 指向的是該類的當(dāng)前對象怕篷。

在擴展函數(shù)或者帶接收者的函數(shù)字面值中, this 表示在點左側(cè)傳遞的 接收者參數(shù)酗昼。
代碼示例:

>>> 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 沒有限定符廊谓,它指的是最內(nèi)層的包含它的作用域。如果我們想要引用其他作用域中的 this麻削,可以使用 this@label 標(biāo)簽蒸痹。
代碼示例:

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 關(guān)鍵字

super關(guān)鍵字持有指向其父類的引用。

代碼示例:

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() // 調(diào)用父類方法
        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 允許我們?yōu)樽约旱念愋吞峁╊A(yù)定義的一組操作符的實現(xiàn)呛哟。這些操作符具有固定的符號表示(如 +*)和固定的優(yōu)先級叠荠。這些操作符的符號定義如下:

    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 操作符優(yōu)先級(Precedence)

優(yōu)先級 標(biāo)題 符號
最高 后綴(Postfix ) ++, --, ., ?., ?
前綴(Prefix) -, +, ++, --, !, labelDefinition@
右手類型運算(Type RHS,right-hand side class type (RHS) ) :, as, as?
乘除取余(Multiplicative) *, /, %
加減(Additive ) +, -
區(qū)間范圍(Range) ..
Infix函數(shù) 例如扫责,給Int定義擴展 infix fun Int.shl(x: Int): Int {...},這樣調(diào)用 1 shl 2榛鼎,等同于1.shl(2)
Elvis操作符 ?:
命名檢查符(Named checks) in, !in, is, !is
比較大小(Comparison) <, >, <=, >=
相等性判斷(Equality) ==, \!==
與 (Conjunction) &&
或 (Disjunction) ll
最低 賦值(Assignment) =, +=, -=, *=, /=, %=

注:Markdown表格語法:ll||公给。

為實現(xiàn)這些的操作符借帘,Kotlin為二元操作符左側(cè)的類型和一元操作符的參數(shù)類型,提供了相應(yīng)的函數(shù)或擴展函數(shù)淌铐。

例如在kotlin/core/builtins/native/kotlin/Primitives.kt代碼中肺然,對基本類型Int的操作符的實現(xiàn)代碼如下

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
}

從源代碼我們可以看出,重載操作符的函數(shù)需要用 operator 修飾符標(biāo)記腿准。中綴操作符的函數(shù)使用infix修飾符標(biāo)記际起。

3.7.5 一元操作符(unary operation)

前綴操作符

表達(dá)式 翻譯為
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()

例如,當(dāng)編譯器處理表達(dá)式 +a 時吐葱,它將執(zhí)行以下步驟:

  • 確定 a 的類型街望,令其為 T
  • 為接收者 T 查找一個帶有 operator 修飾符的無參函數(shù) unaryPlus()弟跑,即成員函數(shù)或擴展函數(shù)灾前。
  • 如果函數(shù)不存在或不明確,則導(dǎo)致編譯錯誤孟辑。
  • 如果函數(shù)存在且其返回類型為 R哎甲,那就表達(dá)式 +a 具有類型 R蔫敲。

編譯器對這些操作以及所有其他操作都針對基本類型做了優(yōu)化,不會引入函數(shù)調(diào)用的開銷炭玫。

以下是如何重載一元減運算符的示例:

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

遞增和遞減

表達(dá)式 翻譯為
a++ a.inc() 返回值是a
a-- a.dec() 返回值是a
++a a.inc() 返回值是a+1
--a a.dec() 返回值是a-1

inc()dec() 函數(shù)必須返回一個值奈嘿,它用于賦值給使用 ++-- 操作的變量。

編譯器執(zhí)行以下步驟來解析后綴形式的操作符吞加,例如 a++

  • 確定 a 的類型裙犹,令其為 T
  • 查找一個適用于類型為 T 的接收者的衔憨、帶有 operator 修飾符的無參數(shù)函數(shù) inc()叶圃。
  • 檢查函數(shù)的返回類型是 T 的子類型。

計算表達(dá)式的步驟是:

  • a 的初始值存儲到臨時存儲 a_
  • a.inc() 結(jié)果賦值給 a
  • a_ 作為表達(dá)式的結(jié)果返回

( a-- 同理分析)巫财。

對于前綴形式 ++a--a 解析步驟類似盗似,但是返回值是取的新值來返回:

  • a.inc() 結(jié)果賦值給 a
  • a 的新值a+1作為表達(dá)式結(jié)果返回

( --a 同理分析)。

3.7.6 二元操作符

算術(shù)運算符

表達(dá)式 翻譯為
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代碼就顯得不大友好)心赶,只能顯式調(diào)用toString來相加:

>>> 1.toString()+""
1

自定義重載的 + 運算符

下面我們使用一個計數(shù)類 Counter 重載的 + 運算符來增加index的計數(shù)值扣讼。

代碼示例

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操作符

表達(dá)式 翻譯為
a in b b.contains(a)
a !in b !b.contains(a)

索引訪問操作符

表達(dá)式 翻譯為
a[i] a.get(i)
a[i] = b a.set(i, b)

方括號轉(zhuǎn)換為調(diào)用帶有適當(dāng)數(shù)量參數(shù)的 getset

調(diào)用操作符

表達(dá)式 翻譯為
a() a.invoke()
a(i) a.invoke(i)

圓括號轉(zhuǎn)換為調(diào)用帶有適當(dāng)數(shù)量參數(shù)的 invoke缨叫。

計算并賦值

表達(dá)式 翻譯為
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 中有兩種類型的相等性:

  • 引用相等 === !==(兩個引用指向同一對象)
  • 結(jié)構(gòu)相等 == !=( 使用equals() 判斷)
表達(dá)式 翻譯為
a == b a?.equals(b) ?: (b === null)
a != b !(a?.equals(b) ?: (b === null))

這個 == 操作符有些特殊:它被翻譯成一個復(fù)雜的表達(dá)式销钝,用于篩選 null 值。

意思是:如果 a 不是 null 則調(diào)用 equals(Any?) 函數(shù)并返回其值琐簇;否則(即 a === null)就計算 b === null 的值并返回蒸健。

當(dāng)與 null 顯式比較時,a == null 會被自動轉(zhuǎn)換為 a=== null

注意===!==不可重載婉商。

Elvis 操作符 ?:

在Kotin中似忧,Elvis操作符特定是跟null比較。也就是說

y = x?:0

等價于

val y = if(x!==null) x else 0

主要用來作null安全性檢查丈秩。

Elvis操作符 ?: 是一個二元運算符盯捌,如果第一個操作數(shù)為真,則返回第一個操作數(shù)蘑秽,否則將計算并返回其第二個操作數(shù)饺著。它是三元條件運算符的變體箫攀。命名靈感來自貓王的發(fā)型風(fēng)格。

Kotlin中沒有這樣的三元運算符 true?1:0瓶籽,取而代之的是if(true) 1 else 0匠童。而Elvis操作符算是精簡版的三元運算符。

我們在Java中使用的三元運算符的語法塑顺,你通常要重復(fù)變量兩次汤求, 示例:

String name = "Elvis Presley";
String displayName = (name != null) ? name : "Unknown";

取而代之,你可以使用Elvis操作符严拒。

String name = "Elvis Presley";
String displayName = name?:"Unknown"

我們可以看出扬绪,用Elvis操作符(?:)可以把帶有默認(rèn)值的if/else結(jié)構(gòu)寫的及其短小。用Elvis操作符不用檢查null(避免了NullPointerException)裤唠,也不用重復(fù)變量挤牛。

這個Elvis操作符功能在Spring 表達(dá)式語言 (SpEL)中提供。

在Kotlin中當(dāng)然就沒有理由不支持這個特性种蘸。

代碼示例:

>>> 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

比較操作符

表達(dá)式 翻譯為
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

所有的比較都轉(zhuǎn)換為對 compareTo 的調(diào)用墓赴,這個函數(shù)需要返回 Int

用infix函數(shù)自定義中綴操作符

我們可以通過自定義infix函數(shù)來實現(xiàn)中綴操作符。

代碼示例

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 函數(shù)擴展和屬性擴展(Extensions)

Kotlin 支持 擴展函數(shù) 和 擴展屬性航瞭。其能夠擴展一個類的新功能而無需繼承該類或使用像裝飾者這樣的設(shè)計模式等诫硕。

大多數(shù)時候我們在頂層定義擴展,即直接在包里:

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

這樣我們就可以在整個包里使用這些擴展刊侯。

要使用其他包的擴展章办,我們需要在調(diào)用方導(dǎo)入它:

package com.example.usage

import foo.bar.goo // 導(dǎo)入所有名為“goo”的擴展
                   // 或者
import foo.bar.*   // 從“foo.bar”導(dǎo)入一切

fun usage(baz: Baz) {
    baz.goo()
}

3.8.1 擴展函數(shù)

聲明一個擴展函數(shù),我們需要用被擴展的類型來作為前綴滨彻。

比如說藕届,我們不喜歡類似下面的雙重否定式的邏輯判斷(繞腦子):

>>> !"123".isEmpty()
true

我們就可以為String類型擴展一個notEmpty()函數(shù):

>>> fun String.notEmpty():Boolean{
... return !this.isEmpty()
... }

>>> "".notEmpty()
false

>>> "123".notEmpty()
true

下面代碼為 MutableList<Int> 添加一個swap 函數(shù):

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // this對應(yīng)該列表
    this[index1] = this[index2]
    this[index2] = tmp
}

這個 this 關(guān)鍵字在擴展函數(shù)內(nèi)部對應(yīng)到接收者對象(傳過來的在點.符號前的對象) 現(xiàn)在,我們對任意 MutableList<Int> 調(diào)用該函數(shù)了亭饵。

當(dāng)然休偶,這個函數(shù)對任何 MutableList<T> 起作用,我們可以泛化它:

    fun <T> MutableList<T>.mswap(index1: Int, index2: Int) {
        val tmp = this[index1] // “this”對應(yīng)該列表
        this[index1] = this[index2]
        this[index2] = tmp
    }

為了在接收者類型表達(dá)式中使用泛型冬骚,我們要在函數(shù)名前聲明泛型參數(shù)椅贱。
完整代碼示例
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對應(yīng)該列表m
this[index1] = this[index2]
this[index2] = tmp
}

fun <T> MutableList<T>.mswap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”對應(yīng)該列表
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()
}
}

擴展不是真正的修改他們所擴展的類。我們定義一個擴展只冻,其實并沒有在一個類中插入新函數(shù)庇麦,僅僅是通過該類型的變量,用點.表達(dá)式去調(diào)用這個新函數(shù)喜德。

3.8.2 擴展屬性

和函數(shù)類似山橄,Kotlin 支持?jǐn)U展屬性:

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)是一件成程序員幾近崩潰的事情。很多時候饮醇,雖然費盡體力腦力它抱,仍然防不勝防。

以前朴艰,當(dāng)我們不確定一個DTO類中的字段是否已初始化時观蓄,可以使用@Nullable和@NotNull注解來聲明,但功能很有限祠墅。

現(xiàn)在好了侮穿,Kotlin在編譯器級別,把你之前在Java中需要寫的null check代碼完成了毁嗦。

但是亲茅,當(dāng)我們的代碼

  • 顯式調(diào)用 throw NullPointerException()
  • 使用了 !! 操作符
  • 調(diào)用的外部 Java 代碼有NPE
  • 對于初始化,有一些數(shù)據(jù)不一致(如一個未初始化的 this 用于構(gòu)造函數(shù)的某個地方)

也可能會發(fā)生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
          ^

這樣腔长,我們就可以放心地調(diào)用 a 的方法或者訪問它的屬性娶耍,不會導(dǎo)致 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 饼酿,然后直接調(diào)用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

我們使用安全調(diào)用?. 和 非空斷言調(diào)用 !!.

>>> na?.length
null
>>> na!!.length
kotlin.KotlinNullPointerException

我們可以看出故俐,代碼返回了null 和 kotlin.KotlinNullPointerException。
安全調(diào)用在鏈?zhǔn)秸{(diào)用中很有用紊婉。在調(diào)用鏈中如果任意一個屬性(環(huán)節(jié))為空药版,這個鏈?zhǔn)秸{(diào)用就會安全返回 null。
如果要只對非空值執(zhí)行某個操作喻犁,安全調(diào)用操作符可以與 let (以調(diào)用者的值作為參數(shù)來執(zhí)行指定的函數(shù)塊槽片,并返回其結(jié)果)一起使用:

>>> val listWithNulls: List<String?> = listOf("A", "B",null)
>>> listWithNulls
[A, B, null]

>>> listWithNulls.forEach{
... it?.let{println(it)}
... }
A
B

本章小結(jié)

本章我們學(xué)習(xí)了Kotlin語言的基本詞匯(關(guān)鍵字、標(biāo)識符等)肢础、句子(流程控制还栓、表達(dá)式、操作符等)和一些基礎(chǔ)語法传轰。同時剩盒,學(xué)習(xí)了空指針安全、擴展函數(shù)與擴展屬性等的語言特性慨蛙。

我們將在下一章節(jié)中介紹Kotlin的基本類型和類型系統(tǒng)辽聊。

參考資料

1.https://www.kotlincn.net/docs/reference/grammar.html

2.https://en.wikipedia.org/wiki/Elvis_operator

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纪挎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子跟匆,更是在濱河造成了極大的恐慌异袄,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玛臂,死亡現(xiàn)場離奇詭異烤蜕,居然都是意外死亡,警方通過查閱死者的電腦和手機垢揩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門玖绿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人叁巨,你說我怎么就攤上這事斑匪。” “怎么了锋勺?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵蚀瘸,是天一觀的道長。 經(jīng)常有香客問我庶橱,道長贮勃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任苏章,我火速辦了婚禮寂嘉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘枫绅。我一直安慰自己泉孩,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布并淋。 她就那樣靜靜地躺著寓搬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪县耽。 梳的紋絲不亂的頭發(fā)上句喷,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音兔毙,去河邊找鬼唾琼。 笑死,一個胖子當(dāng)著我的面吹牛瞒御,可吹牛的內(nèi)容都是我干的父叙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼趾唱!你這毒婦竟也來了涌乳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤甜癞,失蹤者是張志新(化名)和其女友劉穎夕晓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悠咱,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡蒸辆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了析既。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躬贡。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖眼坏,靈堂內(nèi)的尸體忽然破棺而出拂玻,到底是詐尸還是另有隱情,我是刑警寧澤宰译,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布檐蚜,位于F島的核電站,受9級特大地震影響沿侈,放射性物質(zhì)發(fā)生泄漏闯第。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一缀拭、第九天 我趴在偏房一處隱蔽的房頂上張望咳短。 院中可真熱鬧,春花似錦蛛淋、人聲如沸诲泌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哀蘑,卻和暖如春诚卸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绘迁。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工合溺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缀台。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓棠赛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子睛约,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 人生苦多鼎俘,快來 Kotlin ,快速學(xué)習(xí)Kotlin辩涝! 什么是Kotlin贸伐? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,211評論 9 118
  • 本文是在學(xué)習(xí)和使用kotlin時的一些總結(jié)與體會,一些代碼示例來自于網(wǎng)絡(luò)或Kotlin官方文檔怔揩,持續(xù)更新... 對...
    竹塵居士閱讀 3,293評論 0 8
  • 故鄉(xiāng)猶如一幅青白的畫 伴著月色升起在幻夢里 青青的布褂 黑黑的褡褳 那是祖父的顏色 漂浮起嗆人的旱煙味道 圓圓的發(fā)...
    風(fēng)過無痕L閱讀 246評論 1 1
  • 腿基本都比女孩子的細(xì) 羨慕嫉妒恨啊
    林貝貝fy閱讀 222評論 0 1
  • 1 今年的初冬沒有往日的氣勢洶洶捉邢,更像一個半路遇到美女繳械投降的冷面殺手。這萬人迷向大地拋灑火熱的情意商膊,萬物都迷醉...
    辰陽慕雪閱讀 468評論 0 2