[AS3.6.1]Kotlin學習筆記3

前言

kotlin學習第三篇文章!
歷史文章
[AS3.6.1]Kotlin學習筆記1
[AS3.6.1]Kotlin學習筆記2

簡化構造器

我們還是從kotlin的構造器開始講解,前面我都說過kotlin中初始化構造器需要加入新的關鍵字constructor其實在kotlin中我們可以更簡單的創建構造器

//按java寫法的kotlin
class KClass {
    var name: String
    constructor(name: String){
        this.name = name
    }
}
//簡化1
class KClass constructor(name: String){
    var name: String
    init {
        this.name = name    
    }
}
//簡化2
class KClass constructor(name: String){
    var name:String = name
}
//簡化3
class KClass constructor(var name: String){
}
//簡化4
class KClass(var name: String){  
}

我們可以看到kotlin可以簡化到甚至不需要你創建對象和賦值,如果我們需要創建多個構造函數,我們該如何實現呢?比如我們的各種View類,基本上都有三個構造方法,區別如下

//java
public class JView {
    private Context context;
    private AttributeSet attrs;
    private int defStyleAttr;

    public JView(Context context) {
        this(context, null);
    }

    public JView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public JView(Context context, AttributeSet attrs, int defStyleAttr) {
        this.context = context;
        this.attrs = attrs;
        this.defStyleAttr = defStyleAttr;
    }
}

//kotlin
class KView(var context: Context, var attrs: AttributeSet?, var defStyleAttr: Int){

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0){
    }

    constructor(context: Context) : this(context, null){
    }
}
//再次簡化
class KView(var context: Context, var attrs: AttributeSet? = null, 
            var defStyleAttr: Int = 0){
}

可以看到寫法上面是簡潔了非常的多,甚至可以說是一個構造函數實現了多個構造函數。

簡化函數

由上面的簡化構造器,我們發現可以直接爲參數設置默認值實現一個函數實現多個函數的功能。這只是函數的其中一個簡化功能,那麼還有哪些簡化功能呢?下面我們繼續展示

  • 單行函數 = 替代 {}
	//
    fun add(a: Int, b: Int): Int{
        return a + b
    }

	//簡化
    fun add(a: Int, b: Int): Int = a + b
  • 函數設置默認值

上面簡化構造器的時候我們使用過默認值,實際使用的時候,需要設置命名參數纔可以正常使用,比如下例

	//有默認值的函數
    fun add3(a: Int, b: Int = 3, c: Int = 5): Int = a + b + c

	//調用結果
    println(add3(1, 2, 3))	//	1+2+3 = 6
    println(add3(5, c = 6))	//	5+3+6 = 14

這邊看到我們設置了一個命名參數c = 6 就是告訴函數我第二個參數是修改c的值 b還是按照默認值使用。

  • 本地函數(嵌套函數)

可以認爲是不暴露給外部查看,看下面簡單登錄案例

    fun login(user: String, pwd: String){
        if (user.isEmpty()) {
            throw NullPointerException("賬號爲空")
        }
        if (pwd.isEmpty()) {
            throw NullPointerException("密碼爲空")
        }
        println("$user 登錄成功")
    }

我們經常會加一些判斷代碼,如果我們不想暴露就可以加一個嵌套函數來實現

    fun login(user: String, pwd: String){
        fun validate(value: String, error: String) {
            if (value.isEmpty()) {
                throw NullPointerException(error)
            }
        }
        validate(user, "賬號爲空")
        validate(pwd, "密碼爲空")
        println("$user 登錄成功")
    }

這是一個簡單嵌套函數,也說明kotlin的函數可以寫在各個地方和java的方法比地方多了些。

簡化字符串

第一篇我們講到聲明變量的時候只是稍微涉及到一點String,在kotlin中String的使用也簡化了許多

  • 簡化String.format
	//java
	String name = "Java";
    Log.i(name , String.format("你好,%s", name));
	
	//kotlin
	val name = "Kotlin"
    println("你好,$name")

kotlin中的簡化實際是${}這邊因爲是直接打印就省略了{} 實際使用的時候是可以在{}中進行各種簡單的算法判斷等的,這個簡化也順便簡化了getString方法

    <string name="test" formatted="true">測試 %s</string>
    //java
    Log.i("name", getString(R.string.test, "Java"));
	//kotlin
    println(getString(R.string.test, "Kotlin"))
  • 簡化轉譯字符

在java中我們經常需要寫轉譯字符類似\\ \"這些,在kotlin中引入了"""原生字符串來實現

	//java
	String name = "Java";
    Log.i(name , "hello world!");
    Log.i(name , String.format("  我是 %s", name));
    Log.i(name , "    !!!\\n");

	//kotlin
	val name = "Kotlin"
    val str = """hello world!
 我是 $name
   !!!\n"""
    println(str)

打印結果如下

I/Java: hello world!
I/Java:   我是 Java
I/Java:     !!!\n

I/System.out: 你好,KotlinC.kt
I/System.out: hello world!
I/System.out:  我是 張三
I/System.out:    !!!\n

這邊我們看到

  • 回車換行代替了一次打印方法
  • $還是可以直接使用
  • kotlin中的"\n"不等於換行

這邊還有一點就是使用"""之後換行之後只要有回車就需要刪除前面的全部空格,感覺格式非常奇怪,其實是可以使用trimMargin()方法直接刪除前面的空格的,默認截取字段是|

	//修改
	val name = "Kotlin"
    val str = """hello world!
        | 我是 $name
        |   !!!\n""".trimMargin()
    println(str)

操作符簡化

  • 數組集合操作簡化

在java中我們要處理一些數組集合相對比較麻煩,要麼使用Collections對象進行操作,要麼自己遍歷操作,後來RxJava的鏈式模式裏面有了一些方法如filtermap等,在kotlin中也有這些方法

    val intArray = intArrayOf(1, 2, 3, 4, 5)
    val strList = listOf("aa", "b", "cc", "dd", "f")

    intArray.forEach {
        print("$it ")   //1 2 3 4 5
    }
    strList.forEach {
        str -> print("$str ")   //aa b cc dd f
    }

    val newStrList = strList.filter { it.length > 1 }.toList()
    println(newStrList) // aa cc dd

    val newIntArray = intArray.map { it + 1 }.toList()
    println(newIntArray) //2 3 4 5 6

    intArray.reverse()
    for (i in intArray) {
        print("$i ")    //5 4 3 2 1
    }

這邊寫了幾個常用的函數,實際使用中還有很多。

  • 三元運算符簡化

我們再說下?.?:?.我們知道是kotlin的空安全判斷,那麼?:是什麼呢?我們可以理解爲java的? :(三元運算符)

	//java
	String name;
	int len = name == null ? 0 : name.lenght
	
	//kotlin
    var name: String? = null
    val len: Int = name?.length ?: 0
  • 判斷簡化

在java中我們經常用equals來判斷字符串之間是否相等和==來判斷數字之間是否相等,在kotlin中==直接替換了equals==,kotlin新增===來判斷引用的內存地址是否相等,那麼這兩個有什麼區別,案例如下

    val a = "a"
    val b = "b"
    val c = "a"
    println("a == b ${a == b}")     //false
    println("a === b ${a === b}")    //false
    println("a == c ${a == c}")    //true
    println("a === c ${a === c}")    //true

    val d = a
    val e = c
    println("d === e ${d === e}")    //true

我們可以看到==就是直接判斷兩個值是否相等,而===要類型一樣的前提是值也要相等。

泛型簡化

講到泛型我們要先說下java中的泛型,可能我們最經常使用的泛型是List<T extends View>這種類似的寫法,但在java中我們使用泛型的時候經常有? extends? super這兩個關鍵字

? extends

雖然我們經常會寫T extends實際當你不需要設置通配符的時候是可以直接? extends的。比如

    List<? extends View> views = new ArrayList<>(); //自身
    List<? extends View> textViews = new ArrayList<TextView>(); //直接子類
    List<? extends View> buttons = new ArrayList<Button>(); //間接子類

這樣當你需要使用一個父類參數的時候就可以直接使用泛型來擴大方法的使用範圍比如

    private void logId(List<? extends View> views){
        for (View view : views) {
            Log.i("name", "viewId = " + view.getId());
        }
    }

    logId(views);
    logId(textViews);
    logId(buttons);

這樣這個方法讓三個list都可以使用。只能讀取值!

? super

其實? super就是和? extends 剛好相反的概念。? extends 是子類擴展,而? super是父類擴展,案例如下

    List<? super Button> buttons = new ArrayList<>(); //自身
    List<? super Button> textViews = new ArrayList<TextView>(); //直接父類
    List<? super Button> views = new ArrayList<View>(); //間接父類

這父類擴展有什麼用呢?比如一下情況,我們想在一個list之後統一加入一個尾巴按鈕。

    private void addTailButton(List<? super Button> buttons){
        Button button = new Button(ctx);
        buttons.add(button);
    }

    addTailButton(buttons);
    addTailButton(textViews);
    addTailButton(views);

我們使用? super就可以讓快速實現,不需要設置成一個List<View>來改造方法了。

小結

我們發現使用了? extends 的list只能用來獲取值而不能設置值,使用了? super的剛好相反,只能添加值而不能用來獲取值。這被稱爲 PECS 法則:「Producer-Extends, Consumer-Super」

kotlin的泛型

經過上面的說明,我們來看下kotlin中泛型關鍵字inout,他們和java的區別如下

	//java
	List<? extends View> textViews = new ArrayList<TextView>();
	List<? super Button> textViews = new ArrayList<TextView>();

	//kotlin
    val textViews: List<out View> = ArrayList<TextView>()
    val textViews: List<in Button> = ArrayList<TextView>()

在kotlin中就很明顯的告訴了你 PECS 法則,out就在單詞上告訴你我是輸出的(只讀不能寫),in就是輸入的(只寫不能讀)。

我們在java中有時候會經常用到List<?>這樣的寫法代表Object數組因爲List<?>List<? extends Object>的簡寫,在kotlin中我們知道Any代替了Object,那麼在kotlin中的寫法就是List<* out Any>,我們可以看到kotlin中*代替了?,案例如下

	//java
	List<? extends Object> list = new ArrayList<>();
	List<?> list = new ArrayList<>();
	//kotlin
	val list : List<* out Any> = ArrayList<>()
	val list : List<*> = ArrayList<>()

where

在java中如果我們要讓一個泛型必須是多個類的子類是使用&連接,在kotlin中用的是關鍵詞where,代碼如下

	//java
	public class A<T extends VIew & Activity>
	
	//kotlin
	class A<T> where T : View, T : Activity

reified

在java中我們判斷一個對象是否屬於某個泛型會使用isInstance方法,在kotlin中使用的是關鍵詞reifiedinline,代碼如下

	//java
	public <T> void check(Object item, Class<T> type) {
		if (type.isInstance(item)) {
			//....
    	}
	}
	//kotlin
	inline fun <reified T> check(item: Any) {
	    if (item is T) {
	        //...
	    }
	}

總結

本篇基本是kotlin對於java的簡化操作,和語法糖的使用說明!

資料

Kotlin 裏那些「更方便的」
Kotlin 的泛型

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章