Kotlin 筆記(一) 基礎(chǔ)知識點--java對比

創(chuàng)建對象

Java Kotlin
A a = new A() var a = A()

類型聲明

Java Kotlin
String s var s : String
final String s val s : String
class A extends B class A : B
class A implements B class A : B

字符串模板

Java Kotlin
String.format("%s今年%d歲", name, age); "${name}今年${age}歲"

方法

Java Kotlin
String text(int para1,String para2) { ... } fun test(para1: Int, para2: String): String { ... }

Kotlin 特性 : 函數(shù)參數(shù)默認值和可變參數(shù)

對Kotlin函數(shù)中的某個參數(shù)可以用“=”號指定其默認值,調(diào)用函數(shù)方法時可不不傳這個參數(shù),但其他參數(shù)需要用“=”號指定。下文例子中沒有傳遞參數(shù)para2,其實際值為默認值"para2"

fun test(para1: Int, para2: String = "para2", para3: String): String { ... }

test(22, para3 = "hello")

可變參數(shù)值的話,需要用關(guān)鍵字vararg來定義。這里需要注意的是,一個函數(shù)僅能有一個可變參數(shù)。該可變參數(shù)不一定得是最后一個參數(shù),但當這種情況下時,調(diào)用該方法,需要給其他未指明具體值的參數(shù)傳值。

fun test(vararg para1: String, para2: String): String { ... }

test("para1", "para4", "para5", para2 = "hello")

構(gòu)造函數(shù)

Kotlin

類的構(gòu)造函數(shù)分為==primary constructor==和==secondary constructor==,前者只能有一個,而后者可以有多個。如果兩者都未指定,則默認為無參數(shù)的primary constructor。
primary constructor是屬于類頭的一部分,用constructor關(guān)鍵字定義,無可見性字段定義時可省略。初始化代碼需要單獨寫在init代碼塊中,==primary constructor的參數(shù)只能在init代碼塊和變量初始化時使用。==
secondary constructor也是用constructor關(guān)鍵字定義,必須要直接或間接代理primary constructor。

class Student(name: String) { // primary constructor
    var mName: String = name
    init {
        println("Student is called " + name)
    }

    constructor(age: Int, name: String):this(name) {
        println("Student is ${age} years old")
    }
}

繼承

Java Kotlin
Java中類默認可被繼承,只有被final關(guān)鍵字修飾的類才不能被繼承 僅有被open修飾的類才可以被繼承

單例

Java Kotlin
用代碼實現(xiàn)單例 用關(guān)鍵詞 object 定義單例類
object Shop { // 不能有 primary constructor的參數(shù)  ,
    fun buySomething() {
        println("Bought it")
    }
}

Shop.buysomething() // 調(diào)用

靜態(tài)標識與調(diào)用

Java Kotlin
static標識一個類里的靜態(tài)屬性或方法 用companion修飾單例類object,來實現(xiàn)靜態(tài)屬性或方法功能
class Mall(name: String) {

    // companion 伴隨對象 在一個類中 [唯一] 
    companion object Shop { // 與 java 不同,Kotlin 所有靜態(tài)方法和字段都寫在內(nèi)部單例類中
        val SHOP_NAME: String = "McDonald" // 等同于Java中寫public static String
        fun buySomething() { // 等同于Java中寫public static void
            println("Bought it")
        }
    }
    
}


Mall.buySomething() // 可直接調(diào)用,

if-else語句

Kotlin

Kotlin中的if-else語句與Java一致,結(jié)構(gòu)上都是if (條件A) { 條件A為真要執(zhí)行的操作 } else { 條件A為假要執(zhí)行的操作 }
這里主要要介紹的是Kotlin的一個特點,即if-else語句可以作為一個邏輯表達式使用。不僅如此,邏輯表達式還可以以代碼塊的形式出現(xiàn),代碼塊最后的表達式作為該塊的值。

// 邏輯表達式的使用
fun maxNum(x: Int, y: Int): Int {
    var max = if (x > y) x else y
    return max
}
// 代碼塊形式的邏輯表達式
fun maxNumPlus(x: Int, y: Int): Int {
    var max = if (x > y) {
        println("Max number is x")
        x
    } else {
        println("Max number is y")
        y
    }
    return max
}

when語句

Java Kotlin
switch-case語句 有多種形式的條件表達。與if-else一樣Kotlin中的when也可以作為邏輯表達式使用
// 邏輯表達式的使用
fun judge(obj: Any) { // Any 相當于 java中 Object 
    when (obj) {
        1 -> println("是數(shù)字1")
        -1, 0 -> println("是數(shù)字0或-1")
        in 1..10 -> println("是不大于10的正整數(shù)")
        "abc" -> println("是字符串a(chǎn)bc")
        is Double -> println("類型是雙精度浮點數(shù)")
        else -> println("其他操作")
    }
}

標簽

Kotlin中可以對任意表達式進行標簽標記,形式為abc@,xyz@。而這些標簽,可以搭配return、break、continue等跳轉(zhuǎn)行為來使用。

fun labelTest() {
    la@ for (i in 1..10) {
        println("outer index " + i)
        for (j in 1..10) {
            println("inner index " + j )
            if ( inner % 2 == 0) {
                break@la
            }
        }
    }
}

符號“?”

Kotlin中,當我們定義一個變量時,其默認就是非空類型。如果你直接嘗試給他賦值為null,編譯器會直接報錯。Kotlin中將符號“?”定義為安全調(diào)用操作符。變量類型后面跟?號定義,表明這是一個可空類型。同樣的,在調(diào)用子屬性和方法時,也可以用字符?進行安全調(diào)用。Kotlin的編譯器會在寫代碼時就檢查非空情況,因此下文例子中,當s2有前置條件判斷為非空后,即便其本身是可空類型,也可以安全調(diào)用子屬性或方法。對于ifelse結(jié)構(gòu)的邏輯,Kotlin還提供了“?:”操作符,極大了簡化了代碼量又不失可讀性。Kotlin還提供“!!”雙感嘆號操作符來強制調(diào)用對象的屬性和方法,無視其是否非空。這是一個挺危險的操作符,除非有特殊需求,否則為了遠離NPE,還是少用為妙。

var s1: String = "abc"
s1 = null // 這里編譯器會報錯
var s2: String? = "abc"
s2 = null // 編譯器不會報錯

var l1 = s1.length // 可正常編譯
var l2 = s2.length // 沒有做非空判斷,編譯器檢查報錯

if (s2 != null) s2.length // Java式的判空方案
s2?.length // Kotlin的安全調(diào)用操作符?。當s2為null時,s2?.length也為null
if (s2 != null) s2.length else -1 // Java式判空的ifelse邏輯
s2?.length ?: -1 // Kotlin的elvis操作符
s2!!.length // 可能會導(dǎo)致NPE

擴展方法

擴展函數(shù)定義形式

在方法體內(nèi)是可以調(diào)用receiverType的對象中原本的方法

fun receiverType.functionName(params){
    body // 注: 在方法體中使用 this ,那么 this 代表receiverType的對象(調(diào)用者)
}
  • receiverType:表示函數(shù)的接收者,也就是函數(shù)擴展的對象
  • functionName:擴展函數(shù)的名稱
  • params:擴展函數(shù)的參數(shù),可以為NULL

常見例子,其中 this 代表 context ,

fun Context.showLongToast(msg: String) {
    Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
}

擴展方法位置

簡單說可以分為兩種: class 里面 ,class 外面

  1. 在一個類里面寫擴展方法,這時,在其他類直接調(diào)用是不生效的.
class B {

    fun Student.doF(){
        doFly() // Student 中原本就存在的方法
    }

    fun dow2(s:Student){
        s.doF()
    }

}


// 調(diào)用
B().dow2(Student())

在其他類調(diào)用 student.doF() 是不生效的 ,只有創(chuàng)建B對象調(diào)用dow2(s),擴展方法才能被調(diào)用

  1. 單獨新建一文件,或者寫在類的外面,可全部任意調(diào)用,如上面toast的例子,將擴展方法寫在單獨的文件中,在任意activity或者擁有Context的類中都能調(diào)用
/**
* 單獨一個類
*/
package ****

import android.content.Context
import android.widget.Toast
import java.util.*

// 擴展方法
fun Context.showLongToast(msg: String) {
    Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
}


/**
* 一個類內(nèi) ,class 外
*/
fun Context.showLongToast(msg: String) {
    Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
}
class Student(name: String) {
    ...
}


// 調(diào)用
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        showLongToast("111")
    }
}

伴隨對象的擴展

伴隨對象通過“類名.”形式調(diào)用伴隨對象。假如對伴隨對象聲明了擴展函數(shù)該怎么調(diào)用呢?其調(diào)用方式與伴隨對象一樣,都是通過用類名限定符來調(diào)用。當然,擴展屬性也是這樣的。(伴隨對象companion 的使用,請看上文)

fun Student.Shop.doW(){
    println("伴隨對象的擴展函數(shù)")
}


//調(diào)用
Student.doW()

擴展函數(shù)名與原類中的函數(shù)名相同

相同時,調(diào)用時根據(jù)擴展函數(shù)寫的位置不同,會存在兩種情況

  1. 類外.setText()與 TextView 中的方法同名,但是在調(diào)用時,會全部提示出來
fun TextView.setText(msg: String) {
    this.setText("擴展函數(shù)"+msg)
}

但是就傲嬌的不按提示調(diào)用,生寫一個一樣的方法時,這種情況下,總是會優(yōu)先使用成員函數(shù),而不是擴展

open class Person {
    fun doFly() {
        println("親生的")
    }
}

fun Person.doFly() {
    println("后生的")
}

fun main(args: Array<String>) {

    val person: Person = Person()
    person.doFly()
}

// Log打印
親生的
  1. 類內(nèi),通過下面的例子可以得出結(jié)論,當出現(xiàn)重名 doFly() 時,會默認調(diào)用this(也就是 Person)中的方法
open class Person {

    fun doFly() {
        println("Person do fly")
    }
}

class MyInfo {

    fun doRun() {
        println("MyInfo do run")
    }

    fun doFly() {
        println("MyInfo do fly")
    }

    fun Person.doSwim() {
        doFly() // 默認調(diào)用this(也就是 Person)中的方法
        doRun()
    }

    fun doSomething(person: Person) {
        person.doSwim()
    }
}

// 測試實例
fun main(args: Array<String>) {

    val myInfo: MyInfo = MyInfo()
    val person: Person = Person()
    myInfo.doSomething(person)
}
// Log  
Person do fly
MyInfo do run

但是如果也需要調(diào)用 MyInfo 中的doFly(),那么在
doFly()前面加上 "this@MyInfo. " 即可.

  fun Person.doSwim() {
        doFly()
        this@MyInfo.doFly()
        doRun()
}

// Log
Person do fly
MyInfo do fly
MyInfo do run

接收者可為NULL

調(diào)用者對象為空時,也可以調(diào)用

fun Person.doFly() {

    if (null == this) {
        println("null")
    }
    println("doFly")
}

擴展屬性

擴展屬性, 即 Extension Property , 即把某些函數(shù)添加為數(shù)據(jù), 使用”=”, 直接設(shè)置或使用。


val List.lastIndex: Int 
get() = size - 1

注:
由于擴展屬性實際上不會向類添加新的成員, 因此無法讓一個擴展屬性擁有一個后端域變量. 所以,對于擴展屬性不允許存在初始化器. 擴展屬性的行為只能通過明確給定的取值方法與設(shè)值方法來定義,也就意味著擴展屬性只能被聲明為val而不能被聲明為var.如果強制聲明為var,即使進行了初始化,在運行也會報異常錯誤,提示該屬性沒有后端域變量。

數(shù)據(jù)類

Java中數(shù)據(jù)model,通常都是由多個屬性和對應(yīng)的getter、setter組成。當有大量多屬性的數(shù)據(jù)類時,不僅這些類會因為大量的getter和setter方法而行數(shù)爆炸,也使整個工程方法數(shù)驟增。Kotlin中也做了這層特性優(yōu)化,提供了數(shù)據(jù)類的簡單實現(xiàn)。不用再寫get和set方法,這些都由編譯器背后去做,你得到的是一個清爽干凈的數(shù)據(jù)類。具體使用參考下面的例子。

data class Student (
    var name: String,
    var age: Int,
    var hobby: String?,
    var university: String = "NJU"
)
fun printInfo() {
    var s: Student = Student("Ricky", 25, "playing Gwent")
    println("${s.name} is from ${s.university}, ${s.age} years old, and likes ${s.hobby}")
}

為了保證生成代碼的一致性和有意義的方法,數(shù)據(jù)類對象必須滿足一下要求

  • 構(gòu)造函數(shù)必須至少有一個參數(shù)
  • 所有的構(gòu)造函數(shù)參數(shù)必須標注val或者var;
  • 數(shù)據(jù)類對象不能是 abstract, open, sealed or inner;
  • 數(shù)據(jù)類對象不能繼承其他類,但是可以實現(xiàn)接口
  • 在Java虛擬機里,如果生成的類需要有一個無參數(shù)的構(gòu)造函數(shù),所有屬性的默認值必須有一個具體的值,

componentN()

類對象里,有幾個屬性,會有幾個這個方法componentN(),比如上面的示例,調(diào)用方法 s.name 和 s.component1() 效果是一樣的

復(fù)制

我們經(jīng)常有這么一個使用場景:我么需要復(fù)制一個類對象,但是改變它的某些屬性,保持剩余其他的屬性值不變。這就需要用到copy()函數(shù)實現(xiàn)。對于上面的User類,可以如下實現(xiàn)復(fù)制:

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)

// 調(diào)用
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

相關(guān)文檔

Kotlin-擴展;
Kotlin 基礎(chǔ)學(xué)習(xí)+快速實踐

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容