Try   HackMD

Kotlin的keywords和operators

每次回來寫kotlin的時候都會忘記他的一些keywords和operators的用法,這邊簡單記錄幾個Kotlin特有且常用的,讓之後需要寫Kotlin的時候能更快恢復記憶。

官方網站

Keywords and Operators

常用的keywords

  • var
    可變變數

  • val
    不可變變數

  • when
    就是switch

    ​​​when (x) {
    ​​​   1 -> print("x == 1")
    ​​​   2 -> print("x == 2")
    ​​​   else -> { // Note the block
    ​​​       print("x is neither 1 nor 2")
    ​​​   }
    ​​​}
    
  • fun
    宣告function用的

    ​​​fun double(x: Int): Int {
    ​​​  return 2 * x
    ​​​}
    
  • lateinit
    讓var可以延後進行初值化 因為在Kotlin的世界中,class的properties必須一開始就被初值化,但有時候你就是沒辦法立即給一個值,可能你有相關依賴要等待之類的,這時候就可以用lateinit。而若是val需要延後進行initialize的話通常用下面提到的by lazy。 順帶一提,在使用Kotlin開發Android app的時候,不需要使用findviewbyid來將變數與view元件做綁定,直接使用view ID來操控該view就可以了,所以在Android的view上面不需要用lateinit也不需要by lazy。

  • by
    用來實現delegation pattern的keyword,可以簡單理解成provided by by後面接的是要拿來委託做事的instance。 官方的範例是:

    ​​​interface Base {
    ​​​ fun print()
    ​​​}
    
    ​​​class BaseImpl(val x: Int) : Base {
    ​​​    override fun print() { print(x) }
    ​​​}
    
    ​​​class Derived(b: Base) : Base by b
    
    ​​​fun main() {
    ​​​    val b = BaseImpl(10)
    ​​​    Derived(b).print()
    ​​​}
    

    這個例子其實稍微太完整了一點,內容大概是它最後要用一個Derived這個class來做些事情,Derived這個class的其中一個功能是委託給type為Base的b來做,而這個Derived本身也是實作Base這個interface的class,以確保它有print()這個method。 另一個常用且比較簡短的例子是

    ​​​val myLazyString: String by lazy { "Hello" }
    
  • open
    由於Kotlin預設所有class都是final的,所以要讓一個class可以被繼承的話就要先open它。

  • in

    • 用在for-loop for (i in 1..3)
    • 拿來判斷array裡面有無contain什麼元素 if("value" in array)
    • 拿來判斷string裡面有無contain什麼substring if("substring" in string)
    • !in就是不包含的意思
  • as
    val x: String = y as String

  • is
    檢查type用的

  • null
    就是null,記錄一下免得忘了

  • this
    就是this,記錄一下免得忘了

常用的operators

  • :
    用於宣告、繼承以及函數回傳值型別的表達。宣告的時候:的左邊是變數名稱,右邊是型別;繼承的時候:的左邊是子class右邊是父class(小括弧有跟沒有都可以);表達函數回傳值型別的時候:左邊是函數的名字跟參數,就邊是回傳值的型別。
    宣告:

    ​​​val x: String
    ​​​var a: String = "abc"
    

    繼承:

    ​​​// ex1
    ​​​open class Shape {
    ​​​  open fun draw() { /*...*/ }
    ​​​  fun fill() { /*...*/ }
    ​​​}
    
    ​​​class Circle() : Shape() {
    ​​​  override fun draw() { /*...*/ }
    ​​​}
    ​​​
    ​​​// ex2
    ​​​open class Shape {
    ​​​ open val vertexCount: Int = 0
    ​​​}
    
    ​​​class Rectangle : Shape() {
    ​​​  override val vertexCount = 4
    ​​​}
    

    表達函數回傳值型別:

    ​​​fun sum(a: Int, b: Int): Int {
    ​​​    return a+b
    ​​​}
    
  • ?
    Kotlin把變數有分為nullable(可能會是null)跟non-nuuable(不會是null)的,若用?宣告的話該變數就是nullable的。

    ​​​var a: String = "abc" // Regular initialization means non-null by    default
    ​​​a = null // compilation error
    ​​​var b: String? = "abc" // can be set null
    ​​​b = null // ok
    

    然後會常常看到變數後面接?,這時候意思是safe call。其實就是只要是nullable的變數在做操作的時候後面都要加一個?就對了。

    ​​​val a = "Kotlin"
    ​​​val b: String? = null
    ​​​println(b?.length) // print "null"
    ​​​println(a?.length) // print "6", Unnecessary safe call
    
  • !!
    會把變數變成non-null type,在runtime若發現該變數是null的話會丟出一個exception。

    ​​​val l = b!!.length
    
  • ?:
    這叫做Elvis Operator,下兩行程式碼意思相等:

    ​​​val l = b?.length ?: -1
    ​​​val l: Int = if (b != null) b.length else -1
    
  • ==
    value相等判斷,跟常見的語言如C++、Java、Python的一樣,只有相同型別的能判斷,特別的是若是跟null判斷的話永遠都會得到false。順帶一提,Javascript的==會做型別轉換,比較不一樣。

  • ===
    reference相等判斷,用來判別兩個reference是否指向同一個object。順帶一提,Javascrip的===也跟這裡一樣是reference相等的判斷。

  • ..
    會產生一個range,常用於for-loop裡面,以下順便介紹until

    ​​​if (i in 1..4) {  // 1,2,3,4
    ​​​  print(i)
    ​​​}
    ​​​for (i in 1 until 4) { 1,2,3
    ​​​  print(i)
    ​​​}
    
  • ->

    • 用在when裡面
    • 用於function type的表達
      ​​​​​​(A, B) -> C
      ​​​​​​() -> A
      ​​​​​​(int, int) -> int
      ​​​​​​(int) -> Unit
      
      上面程式碼中,左邊的是parameters的型別,右邊是回傳值的型別 右邊放Unit就是沒有回傳值。
    • lambda expression
      ​​​​​​{ a, b -> a + b }
      
      上面程式碼中,左邊的是parameters,右邊是函數內容 完整版的長得像下面的樣子:
      ​​​​​​val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
      
      具體例子如下方Android的Observer:
      ​​​​​​// Create the observer which updates the UI.
      ​​​​​​val nameObserver = Observer<String> { newName ->
      ​​​​​​    // Update the UI, in this case, a TextView.
      ​​​​​​    nameTextView.text = newName
      ​​​​​​}
      
      這個Observer在Observable發送notification時會拿到一個string,也就是newName,此newName就是這個lambda裡面的parameter,然後把它用在lambda的內容裡去更新nameTextView的text。順帶一提,程式碼中Observer<String><String>是generic的表示法,表示這個Observer在implement的時候是用String帶入原本Observer<T>的T。
  • $
    用在string templates

    ​​​val s = "abc"
    ​​​println("$s.length is ${s.length}") // prints "abc.length is 3"
    

以上大概就是比較常用到的,其他的我還太菜目前比較少用到XD