科特林島(Котлин)是一座俄羅斯的島嶼,位於聖彼得堡以西約30千米處,形狀狹長,東西長度約14千米,南北寬度約2千米,面積有16平方千米,扼守俄國進入芬蘭灣的水道。科特林島上建有喀琅施塔得市,爲聖彼得堡下轄的城市。java
Kotlin是一門以kotlin島名命名的現代化編程語言。它是一種針對Java平臺的靜態類型的新編程語言。專一於與Java代碼的互操做性,幾乎能夠應用於現今使用Java的任何地方:服務端開發、Android應用等等。Kotlin能夠很好的和全部現存的java庫和框架一塊兒工做,且性能與Java旗鼓至關。git
谷歌開發者社區作過一個問卷調查,大概有40%的Android開發者已使用過Kotlin。github
Kotlin與Java同樣是一門靜態類型編程語言,在編譯期就已經肯定全部表達式的類型。可是與Java不一樣的一點是Kotlin不須要在源碼中顯示的每一個聲明變量的類型,其所擁有的 類型推導 特性使編譯器經過上下文推斷變量的類型。 以下編程
var xOld: Int = 1
var x = 1
var yOld: String = "string"
var y = "string"
複製代碼
在討論Kotlin中的函數式編程前,咱們先了解一下函數式編程的核心概念:數組
- 頭等函數:把函數看成值來使用和傳遞,可使用變量保存它,也能夠將其做爲其餘函數的參數來進行傳遞,或者將其做爲函數的返回值。
- 不可變性:使用不可變對象,保證該對象的狀態在建立以後不會再變化。
- 無反作用:使用純函數,此類函數在輸入相同參數時會輸出一樣的結果,且不會改變其餘變量的狀態,也不會與外界有任何交互。
因爲Kotlin的首要目標是提供一種更簡介、高效、安全的替代java的語言,因此其擁有java的面向對象特性。同時其豐富的特性集也讓其支持函數式編程的風格。主要特性以下:安全
示例代碼:bash
fun main(args: Array<String>) {
var hello: () -> Unit = { print("Hello world") }
test(hello)
}
fun test(f: () -> Unit) {
f.invoke()
}
>>>
Hello world
複製代碼
fun main(args: Array<String>) {
var square: (Int) -> Int = { it * it }
test(10, square)
}
fun test(i: Int, f: (Int) -> Int) {
print(f(i))
}
>>>
100
複製代碼
//數據類
data class User(val name:String,val age:Int)
複製代碼
Kotlin徹底開源,能夠自由使用,採用Apache2許可證;開發過程徹底公開在Github上。架構
推薦的開發工具:IntelliJ IDEA、Android Studio、Eclipse。框架
Kotlin 不是一門哲學性、研究性語言而是一門實用語言,它的的誕生是爲了解決當前編程世界的許多問題,它的特性也是依據與許多開發者遇到的場景而選擇的。Kotlin開發團隊對於Kotlin能幫助解決實際項目問題的特性頗有自信。編程語言
Kotlin也沒有強制使用某種特定的編程風格和範式。因爲其設計者是JetBrains,Kotlin的開發工具IntelliJ IDEA的插件和編譯器乃是同步開發,且在設計語言特性時就考慮到了對工具的支持,因此毋庸置疑,Kotlin的開發工具對於開發者來講是及其有好的。
在咱們編寫Kotlin過程當中,良好的IDE會發現那些能夠用更簡潔的結構來替換的通用代碼模式,咱們同時也能夠經過研究IDE使用的語言特性,將其應用到本身的代碼中。
Java中常見的代碼在getter
、setter
以及將構造函數的參數賦值給變量的操做都被設置爲隱式的。
var name:String = ""
var name: String
get() {
return name
}
set(value) {
name = value
}
複製代碼
class Person(name: String){
init {
print(name)
}
}
class Student{
constructor(name: String){
print(name)
}
}
複製代碼
Kotlin豐富的標準庫也能夠代替不少沒必要要的冗長的代碼。
例:以下所示的list,需求爲:輸出其中第一個包含字母 b
的item,咱們來看看經過Java和Kotlin的分別是如何實現的。
List<String> list = new ArrayList<>();
list.add("a");
...
list.add("a");
list.add("ab");
複製代碼
Java
for (String s : list) {
if (s.contains("b")) {
System.out.print(s);
break;
}
}
複製代碼
Kotlin
print(list.find { it.contains("b")})
複製代碼
僅需一行代碼就輕鬆搞定了。
還有一個細節須要提醒你們,與不少現代語言同樣,Kotlin中 沒有;
號。
越簡潔的代碼寫起來花的時間越短,更重要的是,讀起來耗費的時間更短,便於提升你的生產力, 使你更快的達到目標。
一般,咱們聲稱一門語言是安全的,安全的含義是它的設計能夠防止程序出現某些類型的錯誤。Kotlin借鑑於Java,提供了一些設計,使其使用起來更加安全。
?
,將其用來定義變量是否爲 null
val s: String= "" //不可爲空
val s1: String? = null //可爲空
複製代碼
if (value is String){
print(value.toUpperCase())
}
複製代碼
在Java項目中添加Kotlin代碼時,不會影響咱們的任何Java代碼。同時,咱們能夠在Kotlin中使用Java的方法、庫。能夠繼承Java的類,實現Java的接口,在Kotlin上使用Java的註解。
另外,Kotlin的互操做性使Java代碼像調用其餘Java類和方法同樣輕鬆的調用Kotlin代碼,在項目任何地方均可以混用Java和Kotlin。
Kotlin最大程度的使用Java庫,使其互操做性更強。好比,Kotlin沒有本身的集合庫,它徹底依賴的Java標準庫,並使用額外的函數來擴展集合功能,使它們在Kotlin中能夠更加便捷的使用。
Kotlin的工具也對互操做性提供了全面支持。能夠編譯任意混合的Java和Kotlin源碼。兩種源文件自由切換,重構某一種語言時,在另一種語言中也會獲得正確的更新。
兩種語言能夠隨意拼接,就像呼吸同樣天然。
在Java中聲明變量時:
int a = 10
複製代碼
在Kotlin中聲明變量時,參數的名稱和類型使用:
分隔,類型在:
以後,稍後咱們會看到函數聲明也是如此。
val a: Int = 10 //顯示指定變量類型
val a = 10 //類型聲明省略
複製代碼
在Kotlin中全部的變量必須有初始值,若是沒有也必須經過延遲初始化或者懶加載方式,在調用變量以前完成賦值操做。
lateinit var name: String
複製代碼
val name by lazy { "liuxi" }
複製代碼
若是你細心的話,會發如今上述的例子中出現了兩種聲明變量的關鍵字:
val
聲明變量,在須要的時候再使用 var
聲明變量Kotlin爲咱們提供了更便捷的字符串拼接方式供咱們使用
val name = "liuxi"
fun age() = 12
val log = "name: $name age: ${age()}"
>>>
name: liuxi age: 12
複製代碼
咱們先來看一下Kotlin中的Hello world。
fun main(args: Array<String>){ //main 是函數名,()內部是參數列表, {}內部是函數體
print("Hello world")
}
複製代碼
Kotlin使用fun
關鍵字來定義函數。
接下來咱們在看一下帶返回類型的函數:
fun max(a: Int,b: Int): Int{
retun if (a > b) a else b
}
複製代碼
函數的返回類型被定義在 :
以後
Kotlin中 if 是有結果值的表達式,不是語句,同時被用來替換掉Java中三元運算符 (boolean值) ? (表達式1) :(表達式2)。
表達式和語句的區別是表達式有值,且能夠做爲另外一個表達式的一部分來使用。
若是一個函數的函數體是由單個表達式構成的,那麼這個函數能夠去掉外層的括號{}
和return
,在函數聲明和表達式中間用=
鏈接。
fun max(a: Int,b: Int) = if(a > b) a else b
//=後面的內容能夠被稱做表達式體
複製代碼
藉助於Kotlin優秀的類型推導特性,在上述範例中,咱們也省略掉了對函數返回類型的聲明。
從Kotlin到Java,不少概念都是相似的。Kotlin 爲了讓函數更簡潔易讀,借鑑Java並作出了不少改,基本以下:
咱們先來看看在Kotlin 中命名參數,默認參數值的使用
命名參數指的是在調用Kotlin定義的函數是,能夠顯示的代表一些參數的名稱。固然若是一個函數由多個參數組成,爲了不混淆,在你給其中一個參數之名名稱以後,其以後的全部參數都須要標明名稱。
fun createAccount(account: String,password: String,desc: String){
...
}
create("liuxi","123456","cool")//常規使用
create("liuxi",password = "123456", desc = "cool") //使用命名參數的方式調用
複製代碼
默認參數值,顧名思義就是一個參數支持聲明默認值,用於避免重載的函數。
好比上述的createAccount()
函數中的desc
是一個非必要參數,這時咱們就能夠給其設置默認值。
fun createAccount(account: String,password: String,desc: String = ""){
...
}
createAccount("liuxi","123456","cool")
createAccount("liuxi","123456")//當用戶不想輸入這個參數時,咱們能夠直接最後一個參數省略掉。
複製代碼
固然,默認參數值的寫法在Java中是不被支持的,調用此方法時仍需顯式的指定全部參數。爲了Java也能夠體會到此方法的便捷性,咱們可使用@JvmOverloads
註解此方法。這樣在Java中會自動生成此方法的重載方法。
頂層函數和屬性的誕生是爲了消除Java中靜態工具類的寫法。即Java中各式各樣的Util工具類。
在Kotlin中,咱們能夠將一些函數和屬性(它們的功能類型使得它們很難歸屬到某一個具體的類中)直接寫在代碼文件(.kt)的頂層,不屬於任何的類。這些函數和屬性依舊是包內的成員。若是從包外想訪問它,則須要Import。 咱們來建立一個Log.kt
文件
package com.liuxi.fun
val KEY = "123456"
fun log(msg:String){ //不須要放置在類體中
Log.d("liuxi_test", msg)
}
複製代碼
在Kotlin中使用此方法就比較便捷:
log("測試頂層函數")
複製代碼
而在Java中,這個文件會被編譯成一個以文件名命名的類,頂層函數會被編譯成這個類的靜態函數。咱們也能夠利用@JvmName
註解去自定義類的名稱。
/*Java*/
package com.liuxi.fun
public class LogKt{
public static final String KEY = "123456"
public static void log(String msg){
Log.d("liuxi_test", msg)
}
}
複製代碼
Kotlin的一個特點就是能夠平滑的與現有代碼集成。當咱們在項目中遇到Java和Kotlin混合使用的時候,爲了便捷的使用一些基於Java的功能,咱們能夠利用擴展函數來對其進行優化。
理論上來講,擴展函數就是類的成員函數,只不過它定義在類的外面。咱們來看一個TextView的擴展函數:結合SpannableString
給TextView
賦值包含一個Image的文本:
/*文件名SpanHelper.kt*/
/**
* 將目標String的替換爲Image
*
* @param mainBody
* @param targetBody
* @param drawableRes
* @return
*/
fun TextView.setTextWithImageSpan(mainBody: String?, targetBody: String?, drawableRes: Int) {
if (mainBody.isNullOrEmpty() || targetBody.isNullOrEmpty() || drawableRes <= 0) {
return
}
val spannableStr = SpannableString(mainBody)
val start = mainBody.indexOf(targetBody)
val end = start + targetBody.length
spannableStr.setSpan(ImageSpan(context, drawableRes), start, end, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
text = spannableStr
}
複製代碼
此擴展方法的調用示例:
textView.setTextWithImageSpan("酷酷的", "酷酷", R.drawable.icon_diamond_small)
複製代碼
簡短的一行代碼,咱們就實現了給TextView設置一段包含圖片的文本。這個功能真的很酷~~~
接下來,咱們來看一下在Java中如何調用擴展函數。
SpanHelperKt.setTextWithImageSpan(textView, "酷酷的", "酷酷", R.drawable.icon_diamond_small)
複製代碼
由於這個函數被聲明爲頂層函數,因此它被編譯成爲靜態函數。textView
被當作靜態函數的第一個參數來使用。
擴展函數不存在重寫,Kotlin會把其看成靜態函數來看待。
接下來咱們來看一下擴展屬性:
//文件名IntUtil.Kt
val Int.square: Int
get() = toInt() * toInt()
fun main(args:Array<String>){
print(5.square)
}
>>> 25
複製代碼
與擴展函數同樣,擴展屬性也像是接收者的一個普通成員屬性同樣。可是必須定義 getter
函數。由於沒有支持的字段,因此沒有默認的getter
。同理由於沒有存儲的地方,因此也沒法初始化。我的感受擴展屬性和擴展函數相似,可是使用的便捷性比擴展函數略遜一籌。
//擴展屬性在Java中的調用
IntUtilKt.getSquare(5)
>>> 25
複製代碼
一種支持在函數內部再定義一個函數的語法:
fun createAccount(name:String,password:String){
fun lengthNotEnough(string:String) = string.length > 10
if(lengthNotEnough(name) return
if(lengthNotEnough(password) return
}
複製代碼
這個功能的意義在於解決代碼重複問題,儘量的保持清晰的代碼結構,提升可讀性。
小知識點:局部函數能夠訪問外層函數的參數。
以下是Collections.kt
中的一個方法
public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()
複製代碼
vararg
修飾符在Kotlin中被用來聲明能夠接受任意數量的參數。
一個頗有趣的語法糖。 一下是我隨手寫的一個例子
infix fun TextView.copy(view: TextView) {
this.text = view.text
}
複製代碼
咱們使用infix
添加在只有一個參數的函數前面。上述例子是在一個擴展函數前面添加上了infix
。 使用範例以下:
val textView1 = ...
val textView2 = ...
textView1 copy textView2 //中綴調用式寫法,這樣textView2的內容就被複制到了textView1 中
textView1.copy(textView2)//常規寫法,與中綴調用式寫法等價。
複製代碼
中綴調用是一種特殊的函數調用,在使用過程當中沒有添加額外的分隔符,函數名稱(copy)直接放置在目標對象和參數之間。
Kotlin中的集合與Java的類相同,可是對擴展函數、內聯函數等對API作了大量的擴展,此處就再也不一一闡述了。
Kotlin中和Java中同樣,使用關鍵字 class
聲明類
class Person{
}
複製代碼
若一個類沒有類體,可上述花括號能夠省略
class Person
複製代碼
另外一個須要注意的地方是:Kotlin中類聲明中沒有了public
。在Kotlin中,類默認爲public和final的
如你所知,在Java中,一個類能夠聲明一個或者多個構造函數,在Kotlin中一樣如此。可是Kotlin對構造函數作了一些修改,引入的主構造方法(在類體外聲明,爲了簡潔的初始化類的操做而被創造出來)和從構造方法(與Java構造方法相似,在類體中聲明)併爲此新增了兩個關鍵字 init
和 constructor
。
關鍵字constructor
用來聲明 構造方法 。
下面是一個用Java式思想和kotlin定義的新語法寫出來的Person類
class Person{
val name: String
constructor(name: String){
this.name = name
}
}
複製代碼
接下來咱們對其引入主構造函數的概念和 init
關鍵字:
class Person constructor(name: String){ //帶一個參數的主構造方法
val name: String
init { //初始化語句塊
this.name = name
}
}
複製代碼
如上代碼所示,init
用來標示初始化語句塊,在類被建立的時候被執行,也可搭配主構造h函數使用。一個類中也能夠聲明多個初始化語句塊,它們按照出如今類體中的順序執行。
接下來,咱們在對Person類進行簡化:
class Person constructor(name: String){
val name: String = name
}
複製代碼
主構造函數的參數同樣可使用 var
或 val
聲明(從構造函數不能夠),咱們可使用以下方式繼續簡化咱們類的屬性定義:
//val 表示相應的屬性會用構造方法的參數來初始化
class Person constructor(val name: String)
複製代碼
若是主構造函數沒有註解或者可見性修飾符,則咱們能夠將constructor
省略。
class Person (val name: String)
複製代碼
在 Kotlin 中的一個類能夠有一個主構造函數以及一個或多個從構造函數。當一個類有主構造函數時,其類體中聲明的從構造函數須要經過直接或者間接的方式委託給主構造函數,此時就須要使用到this
關鍵字。
class Person(val name: String = "liuxi"){
constructor(name: String,age: Int): this(name)
constructor(name: String,age: Int,gender:String): this(name, age)
}
複製代碼
若是你想你的類不被其餘代碼實例化,則能夠給構造函數添加可見型修飾符private
。
class Person private constructor()
複製代碼
val p = Person("liuxi")
複製代碼
注:Kotlin中聲明對象不須要使用 new
關鍵字
咱們先來認識幾個基礎概念:
接口 : 使用 inerface
定義
interface OnClickListener{
fun onClick()
fun test(){ //帶有默認方法體的方法
print("Hello")
}
}
複製代碼
抽象類: 使用abstract
修飾
abstract class Game{
abstract fun play()
open fun record(){ //關鍵字open用來表示這個方法能夠被重寫,open也能夠被用來修飾類。
print("score")
}
fun test(){
print("test")
}
}
複製代碼
接下來咱們來展現一下如何繼承一個抽象類
class A: Game(){
override fun play() = print("Go play")
override fun record(){
super.record() //super 關鍵字用來標示對父類或者接口的默認方法體的引用,相似於Java
}
}
複製代碼
接下來咱們在讓類A
實現接口OnClickListener
:
class A: Game(), OnClickListener{
override fun play() = print("Go play")
override fun record(){
super.record() //super 關鍵字用來標示對父類或者接口的默認方法體的引用,相似於Java
}
override fun onClick()
override fun test(){
super.test()
}
}
複製代碼
如上所示,不管是繼承仍是實現接口,咱們都統一經過 :
來實現,沒有使用Java中的 extends
和 implements
。
其中還須要注意下類的繼承,和接口的實現有一個區別,被繼承的類名後帶了()
,它值得是類的構造方法。若是你的類聲明瞭主構造方法且含有參數,則子類也被強制要求實現主構造方法。此定義相似於Java的子類必須實現父類構造方法,且將父類構造所須要的參數傳遞給父類。
open class A(name:String) //一個類若是非抽象類,則默認是final,不能夠被繼承,可是用open修飾後就能夠了。
class B(name: String): A(name)
複製代碼
open
和 final
是兩個互斥定義的關鍵字。
還有一個細節也須要咱們注意,當你override
父類或者接口的方法後,它們默認是open的,若是你不但願你的子類再去修改它,須要用final
修飾
class A: Game(), OnClickListener{
final override fun play() = print("Go play")
...
}
複製代碼
數據類
data class Person(val name)
複製代碼
內部類和嵌套類
Java:
class A{
public String name = "liuxi";
static class B{
void test(){
System.out.print(name);//此操做被拒絕
}
}
class C{
void test(){
System.out.print(name);
}
}
}
複製代碼
Kotlin:
class A{
var name = "liuxi"
class B{
fun test(){ //爲嵌套類
//print(name),此操做被拒絕,不持有外部類的實例,至關於Java 中的 static class B
}
}
inner class C{
fun test(){ //爲嵌套類
print(name) //持有外部類的實例
}
}
}
複製代碼
密封類 : 定義受限的類繼承結構。這是爲了解決when結構必需要實現一個else分支(相似於Java switch case中的default)而提出的解決方。給父類添加 sealed
修飾符且全部的直接子類必須嵌套在父類中。
sealed class Children{
class Boy: Children()
class Girl:Children()
}
複製代碼
object
關鍵字object
關鍵字在Kotlin中有主要有三個使用場景,其核心理念是:object
定義一個類同時建立一個實例。 接下來,看一下三個場景各是什麼:
object
關鍵字,經過一句話去定義一個類和一個該類的變量。在編譯過程當中,該變量的名字始終都爲INSTANCE
。object Utils{ //因爲類在定義的時候就當即建立了該類的變量,因此該類不被容許擁有構造函數,除此以外,其餘地方都和普通類同樣。
fun test(){
...
}
val TEST = "test"
}
複製代碼
沒有在Java中的各類樣式書寫的單例模式,Kotlin僅需一行代碼,輕鬆搞定。
kotlin中調用以下:
Utils.test()
Utils.TEST
複製代碼
在Java中調用以下:
Utils.INSTANCE.test()
Utils.INSTANCE.TEST
複製代碼
須要經過.INSTANCE
引用對象聲明中的方法,相似於咱們在Java單例模式中習慣去建立的mInstance
Kotlin沒有保留Java中的static
關鍵字,爲了補足靜態函數和屬性的功能,Kotlin提出了伴生對象,頂層函數和屬性(也叫包級函數和屬性),咱們先來說解伴生對象,後者在以後會被提到。
伴生對象是一個聲明在類中的普通對象,能夠擁有名字,也能夠實現接口或者有擴展函數或屬性。
class Person(val name: String){
companion object Loader{
fun test(){
...
}
val type:String = "simple"
}
}
複製代碼
如上範例所示:companion
關鍵字被用來標記伴生對象,伴生對象的名字能夠被省略。簡易寫法以下:
class Person{
companion object{
...
}
}
複製代碼
咱們能夠把伴生對象看成Java中的靜態對象來看待,被定義爲伴生對象後,咱們就能夠經過容器類的名稱來獲取這個對象的調用了。示例以下:
Person.companion.test()
複製代碼
object
來實現匿名內部類object
在類體內部,能夠被用來聲明匿名對象 (替代了Java中的匿名內部類用法)。
常見使用場景有:將一個接口或者抽象類聲明爲匿名對象。
以下是將咱們常見的 OnClikListener
聲明爲匿名對象:
view.setOnClickListener(object: OnClickListener{
override onClick(v:View){
}
})
複製代碼
Java中使用swich
語句來完成對一個值可能出現的不一樣狀況的做出對應處理。在Kotlin中咱們使用when來處理。它比switch擁有更多的使用場景,功能更增強大,使用的也更加頻繁,且還能夠和智能轉換 完美的結合使用。
when和if同樣,都是表達式,能夠擁有返回值。
舉個例子來講:當下有一個需求,咱們須要根據不一樣vip等級返回不一樣的drawable,供給ImageView使用。
val imageView: ImageView = ...
val level: Int = ...
var imageView = imageView.setImageResource(
when (level) {
1 -> R.drawable.icon_level_1
2 -> R.drawable.icon_level_2
3 -> R.drawable.icon_level_3
4 -> R.drawable.icon_level_4
5 -> R.drawable.icon_level_5
else -> {
R.drawable.icon_level_default
}
})
複製代碼
when
語句對於level
的不一樣狀況,when
返回不一樣分支上的值交給setImageResource()
去使用。
值得一提的是,when
支持在一個分支上合併多個選項,好比咱們對上述例子作出修改,將一、2定義爲上,3定義爲中,四、5定義爲下,咱們使用 when
寫出的代碼以下:
...
when (level) {
1,2 -> R.drawable.icon_level_top
3 -> R.drawable.icon_level_middle
4,5 -> R.drawable.icon_level_bottom
else -> {
R.drawable.icon_level_default
}
}
複製代碼
when
比switch
強大的地方在於: 前者容許使用任何對象做爲分支條件,甚至包括無參。可是後者必須使用枚舉常量、字符串或者數字字面值。
open class BaseActivity{
...
}
class MainActivity: BaseActivity(){
...
fun mainPrint(){
print("main")
}
}
class SettingActivity: BaseActivity(){
...
}
複製代碼
fun getTitle(activity: BaseActivity):String = when(activity){
is MainActivity -> {
activity.mainPrint()
"main" //if 和 when 均可以使用代碼塊做爲分支體,在示例狀況下,代碼塊中最後一個表達式就是返回的結果
}
is SettingActivity -> "Setting"
else -> {
""
}
}
複製代碼
is
:用來檢查判斷一個變量是不是某種類型,檢查事後編譯器會自動幫你把該變量轉換成檢查的類型,你能夠直接調用該類型下的方法和變量,此功能能夠被稱做 智能轉換 。它替咱們省略掉了Java 中的instanceOf
檢查後的類型強制轉換這一步,類型判斷和類型轉換二合一的操做也使咱們的代碼更加安全。固然,若是你仍然須要顯式的類型轉換,Kotlin提供了as
來表示到特定類型的顯示轉換。
var a = activity as Maintivity
複製代碼
接下來咱們從一個例子來認識一下 when
的無參寫法。
咱們須要根據兩人團中成員的性別分別給出類型定義,如果都爲男性則稱做男團、都爲女性則成爲女團、男女混合則稱做二人轉。
/*常量*/
const val BOY = 0
const val GIRL = 1
const val BOY_GROUP = "男團"
const val GIRL_GROUP = "女團"
const val MIX_GROUP = "二人轉"
複製代碼
咱們先給出 if
語句的寫法:
fun getGroupName(gender1: Int, gender2: Int) =
if (gender1 != gender2) {
MIX_GROUP
} else if (gender1 == BOY) {
BOY_GROUP
} else {
GIRL_GROUP
}
複製代碼
咱們先給出 when
語句的寫法:
fun getGroupName2(gender1: Int, gender2: Int) =
when {
gender1 != gender2 -> MIX_GROUP
gender1 == BOY -> BOY_GROUP
else -> GIRL_GROUP
}
複製代碼
when
語句的功能特別豐富,相似於Java中的switch
可是功能更增強大。
可空性 是Kotlin類型系統中幫你避免NullPointerException錯誤的特性。
在Java中咱們遇到過太多太多的 java.lang.NullPointerException
,Kotlin解決這類問題的方法是將運行時的異常轉換爲編譯時的異常。經過支持做爲類型學系統的一部分的可控性,編譯器就能在編譯期間發現不少潛在的錯誤,從而減小運行時拋出空指針異常的可能性。
以下是一個獲取字符串長度的方法
fun getLength(str: String?){ //咱們須要在那些可能爲null的實參後面加上問號來標記它
if(str == null){
return 0
}
return str.length
}
複製代碼
問號 ?
能夠加在任何類型的後面,來表示這個類型的變量能夠存儲null
引用。相反沒有被 ?
標記的變量不能存儲null
引用。若是你對一個不接收null
引用的參數賦值爲null
,程序會直接拋出異常 Null can not be a value of a non-null type
。
可空類型和非空類型在Kotlin中有着很嚴格的區分。一旦你的變量被定義爲可空類型,對它的操做也將收到限制,你將沒法調用它的方法,除非你能證實它不爲空;也不能將它賦值給非空類型的變量。也不能將可空類型的參數傳給擁有非空類型參數的函數。
那麼咱們能夠對可空類型參數作什麼呢?咱們對於可空類型參數,最重要的就是和null進行比較。一旦你進行了比較操做,編譯器就會記住。而且在這次比較發生的做用域內把這個值看成非空來對待(咱們就能夠調用非空類型下的參數和函數了),如上述例子。
可空的和非空的對象在運行時沒有什麼區別。可空類型並非非空類型的包裝。全部的檢查都發生在編譯器。即便用Kotlin的可空類型在運行時不會帶來額外的開銷。
?.
安全調用運算符?.
容許把一次null
檢查和一次方法調用合併成一個操做。
str?.toUpperCase()
if(str != null) str.toUpperCase() else null //等效於str?.toUpperCase()
複製代碼
安全運算調符只會調用非空值的方法。 安全調用符的結果類型也是可空的。在上述例子中返回類型爲
String?
?:
在引入可空性概念後,就表明了Kotlin中會出現不少默認值爲null
的可空類型。爲了不默認值weinull
帶來的麻煩,Kotlin提供了 Elvis運算符 ?:
(null合併運算符)
下面展現了它是怎麼使用的:
fun toStr(s: String?){
val str = s ?: "" // 若是s爲null,則賦值爲""
var length = s?.length ?: 0 //常見使用場景,和安全調用運算符搭配使用
}
複製代碼
!!
一個很暴力的語法!非空斷言是最簡單直率的處理可空類型的工具。它使用!!
雙歎號表示。
var length = s!!.length
複製代碼
當一個使用非空斷言的變量爲null時,會顯式的拋出空指針異常。除非你對你的邏輯頗有自信,不要輕易使用這個工具。 常見使用場景是你在一個函數使用一個值時對其進行了非空判斷,而在另外一個函數中使用這個值時,不想重複進行非空判斷,這時你就可使用非空斷言。
let
函數let
函數讓可空表達式的處理變得更加容易。它會把調用它的對象變成一個lambda
表達式。
和安全調用運算符一塊兒使用時,lambda
中的it
爲該對象的非空值,若是該對象爲null
則什麼都不會發生,不爲空時會執行lambda表達式
str?.let{ print(it.length)}
/* 如下是爲了增長可讀性,簡單格式化後的寫法*/
str?.let{ it->
print(it.length)
}
/*常規寫法的等效語句*/
if(str != null){
print(it.length)
}
複製代碼
let
生成的lambda
語句中的it
表明着調用對象的非空值
本文到此也基本告一段落了,寫下這邊文章的主要目的是想讓更多的對Kotlin有興趣的開發人員、在Kotlin和Java中徘徊糾結的人們還有那些對Kotlin抱有各類觀念的人簡單瞭解一下Kotlin,激發起你們學習Kotlin的興趣,也拋開對Kotlin的偏見。看完此篇文章你應該也能夠簡單上手Kotlin了,快快寫起來吧~
文章的定義是入門講解,所以不少地方講解的不夠完善,也不夠詳細。但願那些對Kotlin有興趣的人在學習Kotlin過程當中最好仍是可以系統的學習一邊Kotlin的基礎知識以及進階語法。文章絕大部分知識點參照於《Kotlin實戰》,推薦入門學者翻閱。
一些想寫但沒加入到文章中的知識點(有些部分是我的掌握不熟練,怕描述不許確,還有一點是基於篇幅考慮,怕寫的太長嚇跑新人)
- Kotlin中的lambda表達式,這部分的內容十分豐富,還有不少簡單便捷的語法糖。
- Kotlin 基本數據類型,有點懶就沒寫,但願能夠自行翻閱。
- Kotlin中的集合和數組 以及迭代相關知識(for 、區間、數列、map迭代、in關鍵字)。
- Kotlin的高階函數和泛型。
- 協程(coroutines)是在Kotlin1.3版本中正式發佈,必定要學習~
歡迎你們多多討論,講的晦澀或描述的有問題的地方也懇請你們指正,一塊兒進步~~~~///(^v^)\~~~