Taiwan Kotlin User Group

Logo

Taiwan Kotlin User Group 的網站,在台灣推廣 Kotlin 程式語言,舉辦相關活動。如果對 Kotlin 有興趣,想要多瞭解一些,歡迎來我們的社群一起聚會!

View My GitHub Profile

Kotlin 語法特色

以下來分享一些我們認為 Kotlin 語法上值得關注的特色

data class

data 宣告純資料物件(POJO,Plain Old Java Object)

看起來更加簡潔

data class Customer(
    val name: String,
    val email: String
)

比對 Java 寫法

public class Customer {
    public String name; 
    public String email;
    
    public Customer(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
    public String name() { 
         return this.name;
    }
    
    public String email() { 
         return this.email; 
    }
    // ...
}

var and val

val 宣告不可更改的值(value)

var 宣告可以更改的變數(variable)

讓意外更動常數的機會降低

val a = 1
var b = 2
b = 42


// 編譯錯誤 Val cannot be reassigned
a = 42

fun vs function

fun 關鍵字宣告函數

看起來更加簡潔

fun main() {
}

比對 Java 寫法

public static void main() {
}

default class are final

類別宣告預設為 final

避免超長繼承鏈的出現

class Base(p: Int) 


/*編譯錯誤
This type is final, so it cannot be inherited from
*/
class Derived(p: Int) : Base(p)

: vs extends

使用 : 宣告繼承

看起來更加簡潔

open class Base(p: Int) 

class Derived(p: Int) : Base(p)

no semicolon

不用分號做結尾

看起來更簡潔

也避免常見的漏打分號錯誤

val a = 1


// IDE 提示 Redundant semicolon 
val a = 1;

single return can skip curly brackets

如果函數只有單行

可省略 {}

看起來更加簡潔

fun double(x: Int): Int = x * 2

/*
等同於
fun double(x: Int): Int {
    return x * 2 
}
*/

if expression

Kotlin 的 if 是表達式

可以直接用在變數賦值

val c = if (a > b) a else b

if 還可以直接放在函數回傳內

搭配上單行函數的寫法更加簡潔

fun bigger(a: Int, b: Int) = if (a > b) a else b

when expression

引入 when 表達式

比起 switch 更好看懂

並且避免常見的漏打 break; 錯誤

when (x) {
    1 -> println("x == 1")
    2 -> println("x == 2")
    else -> {
        println("x is neither 1 nor 2")
    }
}

比對 Java 寫法

switch(x) {
    case 1:
        System.out.println("x == 1");
    break;
    case 2:
        System.out.println("x == 2");
    break;
    default:
        System.out.println("x is neither 1 nor 2");
}

whenif 一樣可以直接用在變數賦值

val state = when (x % 3) {  
    0 -> "is a multiple of 3"  
    1 -> "divided by 3 have remainder of 1"  
    2 -> "divided by 3 have remainder of 2"  
    else -> {  
        throw ArithmeticException("impossible!!") 
    }  
}

也可以直接放在函數回傳內

fun getState(x: Int): String = when (x % 3) {  
    0 -> "is a multiple of 3"  
    1 -> "divided by 3 have remainder of 1"  
    2 -> "divided by 3 have remainder of 2"  
    else -> {  
        throw ArithmeticException("impossible!!")  
    }  
}

Singleton object

object 關鍵字宣告單例物件

看起來更加簡潔

object ThisIsASingleton {
    val name: String = "Taiwan Kotlin User Group"
}

比對 Java 寫法

public class ThisIsASingleton {  
    private static final ThisIsASingleton instance = new ThisIsASingleton();  
    private static final String name = "Taiwan Kotlin User Group";  
    private ThisIsASingleton(){}  
    public static ThisIsASingleton getInstance() {  
        return instance;  
    }  
}

Elvis Operator

引入 ?.?:

讓程式可以寫得更簡潔

val b = a?.length ?: -1
// 等同 val b: Int = if (a != null) a.length else -1

Type Inference

Kotlin 可以在編譯時推理變數型態

讓程式不需宣告型態

寫法更加簡潔

val str = "hello"
// 等同 val str : String = "hello"

補充:

Java 在 JDK 10 以後

也支援 Type Inference

var str = "hello";

System.out.println(str); // hello

Type Aliases

引入 typealias 關鍵字

讓程式語意更清晰

typealias CreditCardNumber = String
val aliceCardNumber: CreditCardNumber = "1234****"

Named Arguments

可在呼叫函數時使用參數命名

讓程式的語意更清晰

並避免順序錯誤導致程式錯誤

fun reformat(
    str: String,
    normalizeCase: Boolean = true,
    upperCaseFirstLetter: Boolean = true,
    divideByCamelHumps: Boolean = false,
    wordSeparator: Char = ' ',
) { /*...*/ }

reformat(
    "String!",
    normalizeCase = false,
    upperCaseFirstLetter = false,
    divideByCamelHumps = true,
    wordSeparator = '_'
)

參數命名也讓我們可省略所有含預設值參數

只列出我們需要更動的參數

reformat("String!", upperCaseFirstLetter = false)

/*
等同
reformat(
    "String!",
    true,
    false,
    false,
    ' '
)
*/

Null Check

編譯階段就避免 null 產生問題

減少 NullPointerException 出現

讓程式出錯的機會更少

var output: String
output = null   // 無法通過編譯
val name: String? = null    // 刻意宣告 name 可以為 null
println(name.length())      // 無法通過編譯

Pretty Lambda

容易閱讀的 lambda 函數宣告法

讓程式邏輯易懂又好讀

val printInteger = fun Int.() { println(this) }  
numbers.forEach { printInteger(it) }

比對 Java 寫法

import java.util.function.Consumer;

Consumer<Integer> printInteger = (n) -> { System.out.println(n); };
numbers.forEach( printInteger );

String Interpolation

字串樣板讓程式在字串處理時更加易懂

且不容易出現空白錯誤

val bestLanguage = "Kotlin"
println("Taiwan $bestLanguage User Group")

比對 Java 寫法

String bestLanguage = "Kotlin";  
System.out.println(
    "Taiwan " 
    + bestLanguage 
    + " User Group"
);

Operator Overloading

除了函數可以多載

我們也可以用 operator 關鍵字對符號多載

讓我們的程式更好撰寫

data class Counter(val dayIndex: Int) {  
    operator fun plus(increment: Int): Counter {  
        return Counter(dayIndex + increment)  
    }  
}

val counter = Counter(3)  
println(counter + 4)
// Counter(dayIndex=7)

Infix Notation

引入 infix 關鍵字

讓我們可以宣告更易懂的函數

infix fun Int.shouldEqualTo(actual: Int) {  
    assertEquals(this, actual)  
}

add(2, 2) shouldEqualTo 4

// 等同 assertEquals(4, add(2, 2))

Destructuring Declaration

Kotlin 可以直接解構物件

讓程式邏輯更加簡潔

data class Customer(
    val name: String,
    val email: String
)

val (name, email) = customer

/*
等同
val name = customer.name
val email = customer.email
*/

也可以解構 Map 物件

val map = mapOf("Alice" to 21, "Bob" to 25)
for ((name, age) in map) {
    println("$name is $age years old")          
}

想了解更多 Kotlin 的語法特點嗎?

歡迎加入 kotlin.tips 的 Kotlin 讀書會

也可以參考 Kotlin 慣用寫法