快速上手 Kotlin

前言

其實在剛接觸 Kotlin 的時候,我心裏是拒絕的,因爲看習慣了 Java 的代碼,以致於看 Kotlin 代碼時有一股極大的彆扭感,讓我不想去學習(其實在我學 C 語言的時候也是這個感受)。可是沒辦法呀,誰叫這貨是 Android 官方開發語言呢,再加上如今的招聘要求多多少少也要會 Kotlin 了,因而只能硬着頭皮上咯。java

1. 基本數據類型

相比較 Java ,Kotlin 沒有了基本數據類型,能夠看做是 Java 的基本類型包裝類拿了過來部分改了名字,以下:程序員

Java Kotlin
byte Byte
short Short
int Int
long Long
float Float
double Double
boolean Boolean
char Char

另外字符串是支持模板的,看下面:數組

fun main() {
    val hello = "Hello"
    println("$hello, World!")
    println("length = ${hello.length}")
}

// 輸出:Hello, World!
// length = 5
複製代碼

2. 數組

Kotlin 中沒有了 [] 進行數組聲明或建立,8 種基本數據類型有本身的數組類型,以下:安全

Java Kotlin
byte[] ByteArray
short[] ShortArray
int[] IntArray
long[] LongArray
float[] FloatArray
double[] DoubleArray
boolean[] BooleanArray
char[] CharArray

那引用類型呢?例如咱們要使用一個 String 數組,咱們須要用 Array<T> 這麼一個類,泛型表明數組所屬類型。同理,基本數據類型也能夠用 Array<T> 來聲明,如 Array<Int>Array<Boolean> 等等。bash

初始化既能夠用它們的構造函數,也能夠用 Kotlin 提供的全局函數,arrayOf()emptyArray() ,前者接收一個可變參數,後者則是一個空數組。閉包

3. 訪問權限

publicprotectedprivateinternal 做爲 4 大訪問權限,去除了 defaultide

public 做爲默認訪問權限,所以不少時候咱們能夠不用寫上 public 。函數

protected 相較於 Java 不變。學習

private 除了能夠修飾類級別,其他不變。爲何能夠修飾類呢?這是由於 Kotlin 中一個文件能夠聲明 1 個以上的類,對這些類加上 private 則表示僅能夠在當前文件中使用。ui

internal 表示模塊級別的訪問權限,被 internal 標識則表示僅在當前模塊可見,這在模塊封裝上是頗有幫助的。

4. 繼承性

Kotlin 中,類、字段和函數默認是 final 的,抽象類和接口除外。所以若是咱們想要它們變得可繼承,須要在前面加上 open 關鍵字。

對於普通類,若是想要字段或者函數可繼承,僅在字段或函數上加 open 是不夠的,由於這時候類仍是不可繼承的,所以類也須要加上 open ,這時字段和函數才真正可繼承。

若是不想一個已 open 的字段或函數可繼承,能夠用 final 關鍵字進行修飾。

5. 變量與函數

Kotlin 中將類型進行了後置,什麼意思呢?舉個栗子:

// Java 中咱們這樣聲明變量和函數
public final class Person {
	public final String name;
	public int age;
	
	public final void talk(String str) {
		int temp = 0
	}
}

// Kotlin 中等價於
class Person {
	val name: String? = null
	var age: Int = 0
	
	fun talk(str: String?): Unit {
		var temp = 0
	}
}
複製代碼

首先 Kotlin 中是能夠省略分號的,固然前提是你一個語句一行,若是一行有兩個語句的話就不能省略了,正常狀況下我們都是一行一語句的,接着來分析上面的代碼。

變量

因爲類型後置,所以 Kotlin 使用關鍵字 valvar 來表示變量,val (value) 表示只讀變量,在第一次賦值後就不可修改了;var (variable) 就是咱們在 Java 中經常使用的變量了。

在 Kotlin 中,成員變量是沒有默認值的,須要咱們手動賦值。

另外,Kotlin 是支持類型推斷的,在聲明變量時,能夠不用寫類型,前提是有提供初始值。儘管如此,Kotlin 仍然是強類型語言,類型不匹配照樣會報錯,例如 var age = 0 ,在這個聲明中,儘管咱們沒有顯式提供類型,但初始值代表了這個變量就是一個 Int 變量(整型默認爲 Int),所以後續咱們只能賦 Int 值。

函數

因爲類型後置,所以 Kotlin 使用關鍵字 fun 來表示函數,而且參數列表也遵循類型後置規則,Kotlin 中函數的參數是能夠有默認值的,如:

fun setName(name: String = "aaronzzx"): Unit {
	// ...
}
複製代碼

這個 : Unit 是啥?Unit 至關於 Java 的 void: Unit 表示返回 Unit 類型,至關於沒有返回值,固然更多時候咱們不須要寫上這個多餘的東西,上面的代碼等價於:

fun setName(name: String = "aaronzzx") {
	// ...
}
複製代碼

函數也能夠進行類型推斷,這裏指返回值推斷,僅在簡寫函數時可用,舉個栗子:

fun getAge(): Int {
	return age;
}

// 等價於
fun getAge() = age

// 當咱們的函數只有 1 個語句時,即便沒有返回值,也能夠簡寫。
// 前提這個語句不是賦值語句,否則就 "=" 衝突了
fun function() {
	operate()
}

// 等價於
fun function() = operate()

// 擁有可變參數的函數
fun function(vararg strs: String) {
    for (string in strs) {
        println(string)
    }
}

// 參數爲接口或抽象類
fun initView() {
    HttpUtils.request(object: Callback {
        override fun onSuccess() {
            // ...
        }
        
        override fun onFailure() {
            // ...
        }
    })
}
複製代碼

Ps:你可能有發現類型後面跟了個 ? ,這個表示此變量可被賦值 null ,若是不加問號是沒辦法賦值 null 的,這和 Kotlin 的「空安全」有關,後面會說。

6. 構造函數

Kotlin 的構造函數在第一次看到可能會優勢懵逼,先來看下與 Java 的對比:

// Java
public class Person {
	private String name;
	private int age;
	
	public Person() {
	
	}
	
	public Person(String name) {
		this.name = name;
	}
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
}

// Kotlin
class Person(
	private var name: String? = null,
	private var age: Int = 0
)

// 等價於
class Person(
	name: String? = null,
	age: Int = 0
) {
	private var name: String? = name
	private var age: Int = age
	
	init {
		// 可選
	}
}

// 等價於
class Person() {
	private var name: String? = null
	private var age: Int = 0
	
	constructor(name: String?): this() {
		this.name = name
	}
	
	constructor(name: String?, age: Int): this() {
		this.name = name
		this.age = age
	}
}
複製代碼

在這裏能夠看到,Kotlin 具備碾壓性的優點,對於須要重載構造函數,Kotlin 最少可用 4 行代碼搞定,接下來分析。

首先 Kotlin 能夠省略類體,由於主構造函數被設定在了類名上,主函數能夠有參數,也能夠沒有。若是有參數,能夠在參數名前面加關鍵字 valvar ,表示將這個參數做爲成員變量,在關鍵字前面能夠用訪問權限或註解進行修飾。

init 表示主構造函數的函數體,若是沒有特殊用途能夠被省略。

constructor 表示次構造函數,在擁有主構造函數的狀況下須要顯示調用主構造函數。

7. 空安全

在使用 Java 時,對於 NullPointerException 真的是迫不得已,一不當心就報空指針異常,而後程序就 Crash 了。所以咱們只能常常寫以下的判空代碼:

public String getMobile(Response response) {
    String mobile = null;
    if (response != null) {
        Data data = response.getData();
        if (data != null) {
            mobile = data.getMobile();
        }
    }
    return mobile;
}
複製代碼

或許是知道 Java 程序員處於水深火熱之中,Kotlin 添加了空安全這一語言特性,使用 ? 對可空變量進行標識,而沒被 ? 標識的變量呢?理所固然的是不會爲空了,因而咱們對於不爲空的變量就不用寫繁瑣的判空代碼了,由於它不會出現空指針異常了。

對於可爲空的變量,咱們仍是須要進行判空的,可是形式不與 Java 同樣,因而上面的代碼能夠這麼寫:

fun getMobile(response: Response?): String? {
    return response?.data?.mobile
}
複製代碼

?. 表示若是當前對象不爲空就調用,與 Java 相比,簡潔了好多。

8. 伴生對象

companion object ,這是個啥呢?看下面這段代碼,你應該就明白了。

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val url = intent.getStringExtra(EXTRA_URL)
        // ...
    }
    
    companion object {
        private const val EXTRA_URL = "EXTRA_URL"
        
        fun start(context: Context, url: String) {
            val intent = Intent(context, MainActivity::class.java)
            intent.putExtra(EXTRA_URL, url)
            context.startActivity(intent)
        }
    }
}
複製代碼

說白了,這東西就至關於 Java 的 static ,只是 Kotlin 中沒有 static 這個概念,因此若是咱們要聲明靜態變量或者靜態函數,就須要用到 companion object

另外上面的代碼,可能有些地方你看起來不太明白,這裏說一下。

  • 繼承與實現,統統使用 : 操做符,不一樣的類或接口用 , 隔開,而且須要顯式調用父類構造函數。

  • 首先 Kotlin 中建立對象是省略 new 關鍵字的,直接用構造函數便可。

  • Java 中的 @Override 註解在 Kotlin 中變成了關鍵字 override ,重寫必不可少。

  • 對於無參的 getter 函數,能夠以變量名的形式書寫,例如 getIntent() 就是 intent

  • const val 關鍵字用來聲明常量,僅在 objectcompanion object頂層變量 中可用。

  • object 匿名內部類和單例關鍵字,若是要建立一個單例類,只需這麼寫。

    object Singleton {
        fun exec() {
            // ...
        }
    }
    
    class Test {
        fun function() {
            // 儘管看起來有點像靜態調用,但這就是單例
            Singleton.exec()
        }
    }
    複製代碼
  • 頂層變量,用代碼比較直觀,以下:

    const val TAG = "當前文件"
    
    class MainActivity : AppCompatActivity() {
        // ...
    }
    複製代碼

    頂層變量 屬於文件,不屬於類或接口等等。

9. 分支、循環語句

9.1 分支語句

在 Kotlin 中分支語句屬於表達式,所以咱們能夠這麼寫代碼:

fun function(type: Int) {
    // if-else
    var str = if (type == 1) {
        "Hello, World!"
    } else if (type == 2) {
        "Goodbye!"
    } else ""
    
    // switch ,在 Kotlin 中關鍵字變爲 when
    str = when (type) {
        1 -> "Hello, World!"
        2 -> "Goodbye!"
        else -> ""
    }
    // 或者 when 不帶參數
    str = when {
        type == 1 -> "Hello, World!"
        type == 2 -> "Goodbye!"
        else -> ""
    }
}
複製代碼

當分支語句做爲表達式時,除非你的條件已經包括全部狀況了,不然必定要有 else

9.2 循環語句

whiledo-while 相較於 Java 不變,for 有改動,舉個栗子:

fun function() {
    // for (int i = 0; i < 10; i++)
    // 打印的 i 值 是同樣的
    for (i in 0..9) {
        println(i)
    }
    for (i in 0 until 10) {
        println(i)
    }
    
    // 下面這段將依次打印 10-0
    for (i in 10 downTo 0) {
        println (i)
    }
    
    // 還能夠控制步長
    // 依次打印 0, 2, 4, 6, 8, 10
    for (i in 0..10 step 2) {
        println(i)
    }
}
複製代碼

10. 擴展函數

對於任何類型,咱們均可以擅自給他添加新的函數,而後就能夠用這個類對象調用咱們自定義的擴展函數了,直接看代碼:

fun String.suffix(suffix: String): String {
    return this + suffix
}

fun function() {
    val str = "Hello"
    val newStr = str.suffix(", World!")
    println(str)
    println(newStr)
}

// 將依次輸出:Hello
// Hello, World!
複製代碼

11. 高階函數

高階函數是將函數用做參數或返回值的函數。

在這裏咱們會接觸到一個東西:閉包, 閉包就是可以讀取其餘函數內部變量的函數。

class Operator {
    fun add(a: Int, b: Int) = a + b
    fun minus(a: Int, b: Int) = a - b
    
    fun change( a: Int, b: Int, closure: (Int, Int) -> Int
    ): String {
        return "${closure(a, b)}"
    }
}

fun main() {
    val operator = Operator()
    val add: (Int, Int) -> Int = operator::add
    val str1 = operator.change(2, 2, add)
    val str2 = operator.change(5, 2) { a: Int, b: Int ->
        a - b
    }
    println("str1 = $str1")
    println("str2 = $str2")
}

// 輸出:str1 = 4
// str2 = 3
複製代碼

從 main 函數中,能夠看到變量 add 擁有函數類型 (Int, Int) -> Int ,這意味着這個變量只能被函數賦值,對象名::add 表示拿到這個函數的函數類型。

接着看下面的 change 函數,這個大括號裏面的代碼其實就是閉包,雖然看起來好像是從新定義了一個函數,當一個函數的參數列表最後一個參數是一個閉包時,能夠單獨將閉包抽出來放在小括號後面,若是參數列表只有一個閉包,那麼小括號均可以省略。閉包中,-> 左邊表示閉包的參數,類型能夠省略。

閉包其實看起來有點像回調,咱們先寫好代碼,而後在 change 函數中進行調用。

函數類型能夠沒有參數,可是小括號是不能省略的,包括返回類型也不可省略,例如一個沒有參數沒有返回值的函數類型,能夠這樣表示:() -> Unit

另外函數類型也是可空的,舉個栗子:

fun main() {
    // 當加上?時,函數類型須要用括號括起來
    val function: (() -> Unit)? = {
        // ...
    }
    
    // 調用時須要用 invoke 進行調用
    function?.invoke()
}
複製代碼
相關文章
相關標籤/搜索