Kotlin 學習筆記(1)

看到Android Studio 3.0會內建Kotlin,而且Google也開始以Kotlin為Android的官方語言,怎麼不來了解一下呢?畢竟Koltin 與 Java 都是跑在JVM上的語言相容性極佳,而且兩者可以同時並存,學起來百利無一害,頂多有問題再改回Java來寫,而且Java 與 Koltin之間的呼叫完全沒有問題。

類別宣告

類別宣告方式 Java 與 Koltin的不同

Java 宣告:

public class MainActivity : Activity
{
    final int REQUEST_ID = 1;
    static String TAG = "MainActivity";
    private RecyclerView recyclerView;

    public MainActivity()
    {
    }

    @overrite
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // 變數的宣告
        Button button = new Button(this);
        String name = "Values";
    }
}

Kotlin 宣告:

class MainActivity() : Activity() {

    val REQUEST_ID : Int = 1
    private lateinit var recyclerView : RecyclerView
    companion object {
        var TAG = "MainActivity"
    }

    init {
    }

    override fun onCreate(savedInstanceState: Bundle?) : Unit {
        super.onCreate(savedInstanceState)

        // 變數的宣告
        var button : Button = Button(this)
        var name : String? = "Values" // name 變數允許null
    }
  1. 以建構涵式來說 class MainActivity() : Activity() 就等於 宣告MainActivity 繼承 Activity,又宣告第一個建構式 是不帶參數,建構式的內容則是以init {}的內容。所以當只有一個建構式這樣就夠了。
  2. Koltin 的class 預設是public,並且不允許繼承,必須加上open才可以繼承
  3. 以參數來說 是以 [變數名稱] : [類別型別] 來宣告,後面的 ? 代表此變數允許null
  4. 以函式來說 是以 [有無overrite] fun [function名稱] ([變數]) : [有無回傳值] 來宣告
  5. Java 的void 等同於 Kotlin 的Unit
  6. 每一行程式結束不需要加分號 (;)
  7. 變數的宣告 類似於 TypeScript,var name : String = "Values"來宣告,也可以寫成 var name = "Values"。
  8. Kotlin 的 val 等同於 Java 的 final就是常量
  9. class內的欄位宣告的同時必須要初始化,需要晚點初始化的變數需要加上lateinit關鍵字,lateinit var recyvlerView : RecyclerView。
  10. 不像Java需要寫Button button = new Button(this);,在Koltin只要寫 var button = Button(this),這一點我很不習慣,這樣會跟呼叫函式搞混,怎麼會這樣設計?
  11. Kotlin 移除 static的概念,請將靜態的東西 宣告在 companion 內部。

 

當如果有第二的以上建構式,則以constructor來宣告,冒號後面則可以使用 this / super

    constructor(name: String) : this() {
    
    }

 

資料模型類別

以data class 來宣告,將屬性宣告在建構式之中

data class ProductModel (var Name : String, var Price : Int)

等同於Java的寫法 

public class ProductModel
{
    String Name;
    int Price;

    public void SetName(String name)
    {
        this.Name = name;
    }

    public String GetName()
    {
        return this.Name;
    }
}

 

擴充函式

Kotlin 擁有 Java沒有的擴充函式,有些類別別人已經寫好,你可以重新幫他加上新的方法,func 後面的String.isNullOrEmpty就是幫String新增加一個方法,有沒有看到C#的影子出現

// 不需要像c#一樣,需要定義static class,而是直接寫fun就好,剛開始也是卡在這裡很久,以為要定義class

fun String.isNullOrEmpty() : Boolean {
    return this == null || this.equals("");
}

// 最後就可以這樣使用
"abc".isNullOrEmpty()

 

Lambda

Java 的寫法

// 原本使用匿名物件寫法
Button button = new Button(this);
button.setOnClickListener(new View.OnLongClickListener(){
    @overrite
    public boolean onLongClick(View v){
        ....
        return true;
    }
}

// 使用retrolambda plugin or Java8 特性後
Button button = new Button(this);
button.setOnLongClickListener((v) ->{
        ....
        return true;
    }
}

button.setOnClickListener((View v) ->{
        ....
        return true;
    }
}

Kotlin 的Lambda 是以 { }  包起來,多行的程式碼結尾需要加上分號( ; ),最後一行為回傳值 

var button : Button = Button(this)
// 省略 傳入值
button.setOnLongClickListener { Log.d(TAG, "click"); true; }

// 當傳入值只有一個,可以用it代表傳入值
button.setOnLongClickListener { Log.d(TAG, "click" + it.toString()); true; }

// 宣告傳入值為 v
button.setOnLongClickListener { v -> Log.d(TAG, "click"); true; }

// 完整宣告
button.setOnLongClickListener { v : View -> Log.d(TAG, "click"); true; }
button.setOnLongClickListener { (v : View) -> Log.d(TAG, "click"); true; }


// 匿名物件的宣告
var onClick : View.OnClickListener = object : View.OnClickListener {
    override fun onClick(v: View?) {

    }
}

 

if-else、switch

Java 的 三元運算子

        String name = "Values";

        int length = name!= null ? name.length() : 0;

Kotlin 除了三元運算子 還有 if-else可用 

        var name : String? = "Values" // name 變數允許null

        var length = name != null ? name.length : -1
        var length1 = name?.length() ? : -1
        var length2 = if(name != null) name.length() else -1 // if-else 可以有回傳值,回傳最後一行,類似lambda

 

Java 的 型別檢查 與 轉型

class Child : Father {}


Child c = new Father();
if( c instanceOf Father )
    ((Father) c).DoSomeThing();

Kotlin 的 型別檢查 與轉型 

class Child() : Father() {}


var c : Child = Father()
if( c is Father )
    (c as Father).DoSomeThing()


var example1 = c as Father 
var example2 = c as Father?

var example3 = c as? Father
var example4 = c as? Father?

例子中给出了四种使用as的方式,下面逐行解释:
    1. 将c转换为Father类型的对象,如果c不是可以转换为Father类型的对象,则会抛出异常,如果c为null,也会抛出异常;
    2. 将c转换为Father?类型的对象,,如果c不是可以转换为Father类型的对象,则会抛出异常,如果c为null,则返回null
    3. 将c转换为Father类型的对象,如果c不是可以转换为Father类型的对象,则返回null,如果c为null,则返回null;
    4. 将c转换为Father?类型的对象,如果c不是可以转换为Father类型的对象,则返回null,如果c为null,则返回null.
总结一下,就是as?是尝试性的进行类型转换,如果能转换,则转换,不能转换,则返回null。

 

Java的 switch 在 Kotlin 叫 when,功能更為強大

when(x) {
  is Int -> print(x + 1)
  is String -> print(x.size() + 1)
  is Array<Int> -> print(x.sum())
}

 

Kotlin 的字串模板

val apples = 4
println("I have $apples apples.")

//=========================================

val apples = 4
val bananas = 3

println("I have $apples apples and " + (apples + bananas) + " fruits.") // Java-esque
println("I have $apples apples and ${apples+bananas} fruits.") // Kotlin

 

Kotlin 的區間表達

// Java 的寫法
if (1 <= i && i <= 10) {
  println(i)
} 


if (IntRange(1, 10).contains(i)) {
  println(i)
}

if (i in 1.rangeTo(10)) { ... }
if (i in 1 ... 10) { ... }


for(i in 1..4 step 2) { ... }
for (i in 4 downTo 1 step 2) { ... }