天天看點

Kotlin屬性和字段與基本文法Kotlin屬性和字段與基本文法

Kotlin屬性和字段與基本文法

Kotlin是一門與Swift類似的靜态類型JVM語言,由JetBrains設計開發并開源。與Java相比,Kotlin的文法更簡潔、更具表達性,而且提供了更多的特性,比如,高階函數、操作符重載、字元串模闆。它與Java高度可互操作,可以同時用在一個項目中。

Kotlin屬性和字段與基本文法Kotlin屬性和字段與基本文法

Kotlin屬性和字段

Declaring Properties 聲明屬性

Kotlin中使用var關鍵字聲明可變屬性,或者用val關鍵字聲明隻讀屬性,屬性的類型在後面,變量名在簽名,中間加冒号和空格。

public class Student {
    public var name: String = ...
    public var glass: String = ...
    public var city: String = ...
    public var state: String? = ...
    public var grade: String = ...
}
           

隻需要将成員變量定義成是 public 變量,編譯器會自動生成 getter 和 setter 方法。是以上面的屬性編譯器預設添加了getter 和 setter 方法。

調用的時候與Java一樣,通過變量名直接使用一個屬性

fun recordStudent(student: Student): Student {
    val result = Student() // there's no 'new' keyword in Kotlin
    result.name = student.name
    result.street = student.glass
    // ...
    return result
}
           

上面對屬性的通路,并不是像Java裡面一樣,直接通路屬性的本身,而是預設調用了getter 和 setter 方法。

Getter和Setter

文法中的初始化語句,getter和setter都是可選的,聲明屬性的完整文法如下:

var <propertyName>: <PropertyType> [= <property_initializer>]
    [<getter>]
    [<setter>]
           

其中initializer, getter 和 setter都是可選的。var是允許有getter 和 setter方法,如果變量是val聲明的,它類似于Java中的final,是以如果以val聲明就不允許有setter方法。

自定義通路器(getter)和自定義setter,setter的參數名預設是value,也可以自定義

val isClassSix: Boolean //val 類似于Java中的final
    get() = this.class == 

var stringRepresentation: String
    get() = this.toString()
    set (value) {
        setDataFormString(value) // 格式化字元串,并且将值重新指派給其他元素
    }
           

對于屬性,如果你想改變通路的可見性或者是對其進行注解,但是又不想改變它的預設實作,那麼你就可以定義set和get但不進行實作。

var setterStudentName: String = "美國隊長史愛民" // Initializer required, not a nullable type
  private set // the setter is private and has the default implementation

var setterWithAnnotation: Any?
  @Inject set // annotate the setter with Inject
           

備用字段(Backing Fields)

在上面例子中定義的Student類裡面,屬性的get和set方法裡面使用了一個field,它是一個自動的傳回字段,代表的就是屬性。

Kotlin中的類并不允許使用字段,在自定義getter和setter的時候,可以使用field來起到局部變量的作用。

var counter =  //初始化值會直接寫入備用字段
    get() = field
    set(value) {
        if (value >= )
            field  = value
    }
           

field隻有在通路的時候才會産生,其他時候是不會産生的。

如果Backing Fields不适用的話,其實可以直接使用傳回屬性就可以了。

注意:field辨別符隻允許在屬性的通路器函數内使用.

備用屬性(Backing Properties)

備用屬性,可以看作是備用變量(Backing Fields)的變種,其實際上也是隐含試的對屬性值的初始化聲明,避免了空指針。

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
  get() {
    if (_table == null)
      _table = HashMap() // Type parameters are inferred
    return _table ?: throw AssertionError("Set to null by another thread")
  }
           

不管是備用變量或者備用屬性,都是Kotlin對于空指針的一種解決方案,可以避免函數通路私有屬性而破壞它的結構。

編譯時常量(Compile-Time Constants)

那些在編譯時就能知道具體值的屬性可以使用const修飾符标記為編譯時常量. 這種屬性需要同時滿足以下條件:

  • 頂層或對象的成員(Top-level or member of an object)
  • 以String或基本類型進行初始化
  • 沒有自定義getter
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprected(SUBSYSTEM_DEPRECATED) fun foo() { ... }
           

Top-level(頂級)

Top-level屬性或者方法,與class同級,如下面所示,類名是Kot

package foo.bar

val prop: String = "top-level-prop"
fun demo() {
    loge("top-level", "top-level-demo()")
}

class Kot {
    fun v() {
        loge("top-level", prop)
        demo()
    }
}
           

在編譯成class的時候,會把Top-level的屬性和函數建立到以類名+Kt為名的class檔案中

Top-level調用的時候類似于調用擴充函數那樣,直接調用屬性或者函數。

延遲初始化屬性(Late-Initialized Properties)

在Kotlin中,聲明為具有非空類型的屬性必須在構造函數中初始化,但是往往不希望在構造函數中初始化,例如在通過依賴注入或單元測試的設定方法來初始化屬性的時候,不能在構造器中提供一個非空的初始化語句,為了處理這種情況,就要在屬性上加lateinit關鍵字來延遲初始化。

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()  // dereference directly
    }
}
           

lateinit隻能夠在var類型的屬性中,不能用于構造函數,而且屬性不能有自定義的getter和setting,這些屬性必須是非空類型,并且不能是基本類型。

如果在一個延遲初始化的屬性初始化前調用,會導緻一個特定異常,調用的時候值還沒有初始化.

參考文章:Kotlin官方英文文檔

接下來,我們來學習Kotlin的基本文法

Kotlin的基本文法

1,定義包名(Basic Syntax)

與Java定義包名一樣,在源檔案的開頭定義包名:

//包名和檔案夾路徑可以不一緻:源檔案可以放在項目的任意位置。
package my.demo

import java.util.*

// ...
           

定義函數(Defining functions)

與Java定義函數的差別在于:Kotlin在定義函數的時候要加個fun關鍵詞,函數的傳回值前後不同,Java的在前面,而Kotlin的話,要在後面寫傳回值。

//Java
 private int getResult(int a, int b) {
        return a + b;
    }

 //Kotlin
 private getResult(a: Int, b: Int): Int {
        return a + b
    }
           

如果一個函數隻有一個并且是表達式函數體并且是傳回類型自動推斷的話,可以直接這樣寫

fun getResult(a: Int, b: Int) = a + b

 ```

 如果函數傳回一個無意義的值,相當于Java的void,則可以這樣寫:

 ```
fun getResult(a: Int, b: Int): Unit {
        print(a + b)
    }
           

Uint 的傳回類型可以省略,則上述可以寫成

fun getResult(a: Int, b: Int) {
       print(a + b)
   }
           

定義局部變量(Defining local variables)

Kotlin聲明變量與Java聲明變量有些不一樣,Java變量類型在前,變量名在後,而Kotlin則相反,變量名在前,變量類型在後,中間加:(冒号),并且Kotlin可以自動判斷變量的類型。

聲明局部常量(常量使用val關鍵字,val類似于java中的final)

val a: Int = 
val b =    // 自動判斷出Int類型
val c: Int  // 當沒有初始化值的時候要聲明類型,全局變量不能這樣寫
c =        // 指派
           

聲明變量(變量使用var關鍵字)

var x =  // 自動推斷出Int類型
x += 
           

注釋(Comments)

Kotlin的注釋與Java一樣, 支援單行注釋和塊注釋。

使用字元串模版(Using string templates)

使用${變量},如變量為args: Array”,使用的時候可以這樣寫

fun main(args: Array<String>) {
    if (args.size == ) return

    print("First argument: ${args[0]}")
}
           

而不用再去用加号加起來,gradle裡面也是支援的

fun main(args: Array<String>) {
    if (args.size == ) return

    print("First argument: " + args[])
}
           

使用條件表達式(Using conditional expressions)

使用if表達式的時候這樣寫

fun max(a: Int, b: Int): Int {
    if (a > b) {
        return a
    } else {
        return b
    }
}
           

也可以寫成:

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

使用可空變量和空置檢查(Using nullable values and checking for null)

當一個變量、函數可能出現空值的時候,應該指出該引用可空,如:

fun getResult(a: Int, b: Int): Int? { // 設定函數傳回類型可空
        print(a + b)
        return null
    }
           

使用類型檢查和自動轉換(Using type checks and automatic casts)

is運算符是檢查一個表達式是否是某個類型的執行個體,如果為不可變局部變量或屬性進行類型檢查,則無需顯式轉換

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj`在這個分支中自動轉換為`String`類型
        return obj.length
    }

    // `obj`仍然是`Any`類型
    return null
}
           

或者

fun getStringLength(obj: Any): Int? {
    if (obj !is String) return null

    // `obj`在這個分支中自動轉換為`String`類型
    return obj.length
}
           

也可以這樣

fun getStringLength(obj: Any): Int? {
    // `obj` 自動轉換成`String`類型,并且在&&後面生效
    if (obj is String && obj.length > ) {
        return obj.length
    }

    return null
}
           

使用for循環(Using a for loop)

與Java相比,Kotlin使用for循環要更加的簡潔,這點和Python的簡潔很相似

fun main(args: Array<String>) {
    for (arg in args) { // in操作符可以判斷是否arg是否在args裡面
        print(arg)
    }
}
           

或者這樣寫

for (i in args.indices) {
    print(args[i])
}
           

使用while循環(Using a while loop)

while循環與Java一樣

fun main(args: Array<String>) {
    var i = 
    while (i < args.size) {
        print(args[i++])
    }  
}
           

使用when表達式(Using when expression)

when表達式就相當于Java的switch表達式,省去了case和break,并且支援各種類型。

fun cases(obj: Any) {
    when (obj) {
                  -> print("One") //如果obj的值為
        "Hello"    -> print("Greeting") // 如果obj的值為hello
        is Long    -> print("Long") // 如果obj的類型是Long類型
        !is String -> print("Not a string") // 如果obj的類型不屬于String類型
        else       -> print("Unknown") // 預設,相當于switch的default
    }
}
           

使用ranges(Using ranges)

使用in運算符檢查數字是否在範圍内

if (x in .y-) { //.y-表示到y-的範圍
    print("OK")
}
for (i in ) { ... }  //  到範圍
for (i in  until ) { ... } // 半開範圍,不包括,相當于[,)
for (x in  step ) { ... } // 每次誇,内容為,,,,
for (x in  downTo ) { ... } // 返序
if (x in ) { ... }
           

使用!in運算符檢查數值是否在範圍外

if (x !in .array.lastIndex) {
    print("Out")
}
           

使用集合(Using collections)

循環輸出一個集合裡面的值或者判斷集合裡面是否包含某個變量

for (name in names) { // 将會調用nemes.contains(name)方法
    println(name)
}
           

使用lambda表達式過濾和映射集合

// it表示name
names
        .filter { it.startsWith("A") }
        .sortedBy { it }
        .map { it.toUpperCase() }
        .forEach { print(it) }
           

好了結合了Kotlin的屬性和字段與基礎文法,現在是不是讓你有了打開了新世界大門的感觸?

繼續閱讀