Kotlin 類和物件
類定義
Kotlin 類可以包含:建構函式和初始化程式碼塊、函式、屬性、內部類、物件宣告。
Kotlin 中使用關鍵字 class 宣告類,後面緊跟類名:
class itread01 { // 類名為 itread01 // 大括號內是類體構成 }
我們也可以定義一個空類:
class Empty
可以在類中定義成員函式:
class itread01() { fun foo() { print("Foo") } // 成員函式 }
類的屬性
屬性定義
類的屬性可以用關鍵字 var 宣告為可變的,否則使用只讀關鍵字 val 宣告為不可變。
class itread01 { var name: String = …… var url: String = …… var city: String = …… }
我們可以像使用普通函式那樣使用建構函式建立類例項:
val site = itread01() // Kotlin 中沒有 new 關鍵字
要使用一個屬性,只要用名稱引用它即可
site.name // 使用 . 號來引用 site.url
Koltin 中的類可以有一個 主構造器,以及一個或多個次構造器,主構造器是類頭部的一部分,位於類名稱之後:
class Person constructor(firstName: String) {}
如果主構造器沒有任何註解,也沒有任何可見度修飾符,那麼constructor關鍵字可以省略。
class Person(firstName: String) { }
getter 和 setter
屬性宣告的完整語法:
var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>]
getter 和 setter 都是可選
如果屬性型別可以從初始化語句或者類的成員函式中推斷出來,那就可以省去型別,val不允許設定setter函式,因為它是隻讀的。
var allByDefault: Int? // 錯誤: 需要一個初始化語句, 預設實現了 getter 和 setter 方法 var initialized = 1 // 型別為 Int, 預設實現了 getter 和 setter val simple: Int? // 型別為 Int ,預設實現 getter ,但必須在建構函式中初始化 val inferredType = 1 // 型別為 Int 型別,預設實現 getter
例項
以下例項定義了一個 Person 類,包含兩個可變變數 lastName 和 no,lastName 修改了 getter 方法,no 修改了 setter 方法。
class Person { var lastName: String = "zhang" get() = field.toUpperCase() // 將變數賦值後轉換為大寫 set var no: Int = 100 get() = field // 後端變數 set(value) { if (value < 10) { // 如果傳入的值小於 10 返回該值 field = value } else { field = -1 // 如果傳入的值大於等於 10 返回 -1 } } var heiht: Float = 145.4f private set } // 測試 fun main(args: Array<String>) { var person: Person = Person() person.lastName = "wang" println("lastName:${person.lastName}") person.no = 9 println("no:${person.no}") person.no = 20 println("no:${person.no}") }
輸出結果為:
lastName:WANG no:9 no:-1
Kotlin 中類不能有欄位。提供了 Backing Fields(後端變數) 機制,備用欄位使用field關鍵字宣告,field 關鍵詞只能用於屬性的訪問器,如以上例項:
var no: Int = 100 get() = field // 後端變數 set(value) { if (value < 10) { // 如果傳入的值小於 10 返回該值 field = value } else { field = -1 // 如果傳入的值大於等於 10 返回 -1 } }
非空屬性必須在定義的時候初始化,kotlin提供了一種可以延遲初始化的方案,使用 lateinit 關鍵字描述屬性:
public class MyTest { lateinit var subject: TestSubject @SetUp fun setup() { subject = TestSubject() } @Test fun test() { subject.method() // dereference directly } }
主構造器
主構造器中不能包含任何程式碼,初始化程式碼可以放在初始化程式碼段中,初始化程式碼段使用 init 關鍵字作為字首。
class Person constructor(firstName: String) { init { println("FirstName is $firstName") } }
注意:主構造器的引數可以在初始化程式碼段中使用,也可以在類主體n定義的屬性初始化程式碼中使用。 一種簡潔語法,可以通過主構造器來定義屬性並初始化屬性值(可以是var或val):
class People(val firstName: String, val lastName: String) { //... }
如果構造器有註解,或者有可見度修飾符,這時constructor關鍵字是必須的,註解和修飾符要放在它之前。
例項
建立一個 itread01類,並通過建構函式傳入網站名:
class itread01 constructor(name: String) { // 類名為 itread01 // 大括號內是類體構成 var url: String = "http://www.itread01.com" var country: String = "CN" var siteName = name init { println("初始化網站名: ${name}") } fun printTest() { println("我是類的函式") } } fun main(args: Array<String>) { val itread01 = itread01("入門教學") println(itread01.siteName) println(itread01.url) println(itread01.country) itread01.printTest() }
輸出結果為:
初始化網站名: 入門教學 入門教學 http://www.itread01.com CN 我是類的函式
次建構函式
類也可以有二級建構函式,需要加字首 constructor:
class Person { constructor(parent: Person) { parent.children.add(this) } }
如果類有主建構函式,每個次建構函式都要,或直接或間接通過另一個次建構函式代理主建構函式。在同一個類中代理另一個建構函式使用 this 關鍵字:
class Person(val name: String) { constructor (name: String, age:Int) : this(name) { // 初始化... } }
如果一個非抽象類沒有宣告建構函式(主建構函式或次建構函式),它會產生一個沒有引數的建構函式。建構函式是 public 。如果你不想你的類有公共的建構函式,你就得宣告一個空的主建構函式:
class DontCreateMe private constructor () { }
注意:在 JVM 虛擬機器中,如果主建構函式的所有引數都有預設值,編譯器會生成一個附加的無參的建構函式,這個建構函式會直接使用預設值。這使得 Kotlin 可以更簡單的使用像 Jackson 或者 JPA 這樣使用無參建構函式來建立類例項的庫。
class Customer(val customerName: String = "")
例項
class itread01 constructor(name: String) { // 類名為 itread01 // 大括號內是類體構成 var url: String = "http://www.itread01.com" var country: String = "CN" var siteName = name init { println("初始化網站名: ${name}") } // 次建構函式 constructor (name: String, alexa: Int) : this(name) { println("Alexa 排名 $alexa") } fun printTest() { println("我是類的函式") } } fun main(args: Array<String>) { val itread01 = itread01("入門教學", 10000) println(itread01.siteName) println(itread01.url) println(itread01.country) itread01.printTest() }
輸出結果為:
初始化網站名: 入門教學 Alexa 排名 10000 入門教學 http://www.itread01.com CN 我是類的函式
抽象類
抽象是面向物件程式設計的特徵之一,類本身,或類中的部分成員,都可以宣告為abstract的。抽象成員在類中不存在具體的實現。
注意:無需對抽象類或抽象成員標註open註解。
open class Base { open fun f() {} } abstract class Derived : Base() { override abstract fun f() }
巢狀類
我們可以把類巢狀在其他類中,看以下例項:
class Outer { // 外部類 private val bar: Int = 1 class Nested { // 巢狀類 fun foo() = 2 } } fun main(args: Array<String>) { val demo = Outer.Nested().foo() // 呼叫格式:外部類.巢狀類.巢狀類方法/屬性 println(demo) // == 2 }
內部類
內部類使用 inner 關鍵字來表示。
內部類會帶有一個對外部類的物件的引用,所以內部類可以訪問外部類成員屬性和成員函式。
class Outer { private val bar: Int = 1 var v = "成員屬性" /**巢狀內部類**/ inner class Inner { fun foo() = bar // 訪問外部類成員 fun innerTest() { var o = this@Outer //獲取外部類的成員變數 println("內部類可以引用外部類的成員,例如:" + o.v) } } } fun main(args: Array<String>) { val demo = Outer().Inner().foo() println(demo) // 1 val demo2 = Outer().Inner().innerTest() println(demo2) // 內部類可以引用外部類的成員,例如:成員屬性 }
為了消除歧義,要訪問來自外部作用域的 this,我們使用this@label,其中 @label 是一個 代指 this 來源的標籤。
匿名內部類
使用物件表示式來建立匿名內部類:
class Test { var v = "成員屬性" fun setInterFace(test: TestInterFace) { test.test() } } /** * 定義介面 */ interface TestInterFace { fun test() } fun main(args: Array<String>) { var test = Test() /** * 採用物件表示式來建立介面物件,即匿名內部類的例項。 */ test.setInterFace(object : TestInterFace { override fun test() { println("物件表示式建立匿名內部類的例項") } }) }
類的修飾符
類的修飾符包括 classModifier 和_accessModifier_:
classModifier: 類屬性修飾符,標示類本身特性。
abstract // 抽象類 final // 類不可繼承,預設屬性 enum // 列舉類 open // 類可繼承,類預設是final的 annotation // 註解類
accessModifier: 訪問許可權修飾符
private // 僅在同一個檔案中可見 protected // 同一個檔案中或子類可見 public // 所有呼叫的地方都可見 internal // 同一個模組中可見
例項
// 檔名:example.kt package foo private fun foo() {} // 在 example.kt 內可見 public var bar: Int = 5 // 該屬性隨處可見 internal val baz = 6 // 相同模組內可見