類(lèi)和對象是兩種以計算機為載體的計算機語(yǔ)言的合稱(chēng)。對象是對客觀(guān)事物的抽象,類(lèi)是對對象的抽象。類(lèi)是一種抽象的數據類(lèi)型。它們的關(guān)系是,對象是類(lèi)的實(shí)例,類(lèi)是對象的模板。
類(lèi)定義
Kotlin類(lèi)可以包含:構造函數和初始化代碼塊、函數、屬性、內部類(lèi)、對象聲明。Kotlin中使用關(guān)鍵字class聲明類(lèi),后面緊跟類(lèi)名:
// 類(lèi)名為 Runoobclass Runoob { // 大括號內是類(lèi)體構成}
我們也可以定義一個(gè)空類(lèi):
class Empty
可以在類(lèi)中定義成員函數:
class Runoob() { // 成員函數 fun foo() { print("Foo") }}
類(lèi)的屬性
類(lèi)的屬性可以用關(guān)鍵字var聲明為可變的,否則使用只讀關(guān)鍵字val聲明為不可變。
class Runoob { var name: String = …… var url: String = …… var city: String = ……}
我們可以像使用普通函數那樣使用構造函數創(chuàng )建類(lèi)實(shí)例:
// Kotlin中沒(méi)有new關(guān)鍵字val site = Runoob()
要使用一個(gè)屬性,只要用名稱(chēng)引用它即可
site.name // 使用.號來(lái)引用site.url
Koltin中的類(lèi)可以有一個(gè)主構造器,以及一個(gè)或多個(gè)次構造器,主構造器是類(lèi)頭部的一部分,位于類(lèi)名稱(chēng)之后:
class Person constructor(firstName: String) {}
如果主構造器沒(méi)有任何注解,也沒(méi)有任何可見(jiàn)度修飾符,那么constructor關(guān)鍵字可以省略。
class Person(firstName: String) {}
getter和setter
屬性聲明的完整語(yǔ)法:
var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>]
getter和setter都是可選。如果屬性類(lèi)型可以從初始化語(yǔ)句或者類(lèi)的成員函數中推斷出來(lái),那就可以省去類(lèi)型,val不允許設置setter函數,因為它是只讀的。
// 錯誤:需要一個(gè)初始化語(yǔ)句,默認實(shí)現了getter和setter方法var allByDefault: Int?// 類(lèi)型為Int,默認實(shí)現了getter和settervar initialized = 1// 類(lèi)型為Int,默認實(shí)現getter,但必須在構造函數中初始化val simple: Int?// 類(lèi)型為Int類(lèi)型,默認實(shí)現getterval inferredType = 1
實(shí)例
以下實(shí)例定義了一個(gè)Person類(lèi),包含兩個(gè)可變變量lastName和no,lastName修改了getter方法,no修改了setter方法。
class Person { var lastName: String = "zhang" get() = field.toUpperCase() // 將變量賦值后轉換為大寫(xiě) 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中類(lèi)不能有字段。提供了BackingFields(后端變量)機制,備用字段使用field關(guān)鍵字聲明,field關(guān)鍵詞只能用于屬性的訪(fǎng)問(wèn)器,如以上實(shí)例:
var no: Int = 100 get() = field // 后端變量 set(value) { if (value < 10) { // 如果傳入的值小于10返回該值 field = value } else { field = -1 // 如果傳入的值大于等于10返回-1 } }
非空屬性必須在定義的時(shí)候初始化,kotlin提供了一種可以延遲初始化的方案,使用 lateinit 關(guān)鍵字描述屬性:
public class MyTest { lateinit var subject: TestSubject
@SetUp fun setup() { subject = TestSubject() }
@Test fun test() { subject.method() // dereference directly }}
廣告時(shí)間,休息一下
主構造器
主構造器中不能包含任何代碼,初始化代碼可以放在初始化代碼段中,初始化代碼段使用init關(guān)鍵字作為前綴。
class Person constructor(firstName: String) { init { println("FirstName is $firstName") }}
注意:主構造器的參數可以在初始化代碼段中使用,也可以在類(lèi)主體n定義的屬性初始化代碼中使用。一種簡(jiǎn)潔語(yǔ)法,可以通過(guò)主構造器來(lái)定義屬性并初始化屬性值(可以是var或val):
class People(val firstName: String, val lastName: String) { //...}
如果構造器有注解,或者有可見(jiàn)度修飾符,這時(shí)constructor關(guān)鍵字是必須的,注解和修飾符要放在它之前。
實(shí)例
創(chuàng )建一個(gè)Runoob類(lèi),并通過(guò)構造函數傳入網(wǎng)站名:
class Runoob constructor(name: String) { // 類(lèi)名為Runoob // 大括號內是類(lèi)體構成 var url: String = "http://www.github.com" var country: String = "CN" var siteName = name
init { println("初始化網(wǎng)站名: ${name}") }
fun printTest() { println("我是類(lèi)的函數") }}
fun main(args: Array<String>) { val runoob = Runoob("菜鳥(niǎo)教程") println(runoob.siteName) println(runoob.url) println(runoob.country) runoob.printTest()}
輸出結果為:
初始化網(wǎng)站名: 菜鳥(niǎo)教程
菜鳥(niǎo)教程
http://www.github.com
CN
我是類(lèi)的函數
次構造函數
類(lèi)也可以有二級構造函數,需要加前綴constructor
class Person { constructor(parent: Person) { parent.children.add(this) }}
如果類(lèi)有主構造函數,每個(gè)次構造函數都要,或直接或間接通過(guò)另一個(gè)次構造函數代理主構造函數。在同一個(gè)類(lèi)中代理另一個(gè)構造函數使用this關(guān)鍵字:
class Person(val name: String) { constructor (name: String, age:Int) : this(name) { // 初始化... }}
如果一個(gè)非抽象類(lèi)沒(méi)有聲明構造函數(主構造函數或次構造函數),它會(huì )產(chǎn)生一個(gè)沒(méi)有參數的構造函數。構造函數是public。如果你不想你的類(lèi)有公共的構造函數,你就得聲明一個(gè)空的主構造函數:
class DontCreateMe private constructor () {}
注意:在JVM虛擬機中,如果主構造函數的所有參數都有默認值,編譯器會(huì )生成一個(gè)附加的無(wú)參的構造函數,這個(gè)構造函數會(huì )直接使用默認值。這使得Kotlin可以更簡(jiǎn)單的使用像Jackson或者JPA這樣使用無(wú)參構造函數來(lái)創(chuàng )建類(lèi)實(shí)例的庫。
class Customer(val customerName: String = "")
實(shí)例
// 類(lèi)名為Runoobclass Runoob constructor(name: String) { // 大括號內是類(lèi)體構成 var url: String = "http://www.github.com" var country: String = "CN" var siteName = name
init { println("初始化網(wǎng)站名: ${name}") } // 次構造函數 constructor (name: String, alexa: Int) : this(name) { println("Alexa 排名 $alexa") }
fun printTest() { println("我是類(lèi)的函數") }}
fun main(args: Array<String>) { val runoob = Runoob("菜鳥(niǎo)教程", 10000) println(runoob.siteName) println(runoob.url) println(runoob.country) runoob.printTest()}
輸出結果為:
初始化網(wǎng)站名: 菜鳥(niǎo)教程
Alexa 排名 10000
菜鳥(niǎo)教程
http://www.github.com
CN
我是類(lèi)的函數
抽象類(lèi)
抽象是面向對象編程的特征之一,類(lèi)本身,或類(lèi)中的部分成員,都可以聲明為abstract的。抽象成員在類(lèi)中不存在具體的實(shí)現。注意:無(wú)需對抽象類(lèi)或抽象成員標注open注解。
open class Base { open fun f() {}}
abstract class Derived : Base() { override abstract fun f()}
嵌套類(lèi)
我們可以把類(lèi)嵌套在其他類(lèi)中,看以下實(shí)例:
// 外部類(lèi)class Outer { private val bar: Int = 1 // 嵌套類(lèi) class Nested { fun foo() = 2 }}
fun main(args: Array<String>) { // 調用格式:外部類(lèi).嵌套類(lèi).嵌套類(lèi)方法/屬性 val demo = Outer.Nested().foo() println(demo) // 2}
內部類(lèi)
內部類(lèi)使用inner關(guān)鍵字來(lái)表示。內部類(lèi)會(huì )帶有一個(gè)對外部類(lèi)的對象的引用,所以?xún)炔款?lèi)可以訪(fǎng)問(wèn)外部類(lèi)成員屬性和成員函數。
class Outer { private val bar: Int = 1 var v = "成員屬性" // 嵌套內部類(lèi) inner class Inner { fun foo() = bar // 訪(fǎng)問(wèn)外部類(lèi)成員 fun innerTest() { var o = this@Outer // 獲取外部類(lèi)的成員變量 println("內部類(lèi)可以引用外部類(lèi)的成員,例如:" + o.v) } }}
fun main(args: Array<String>) { val demo = Outer().Inner().foo() println(demo) // 1 val demo2 = Outer().Inner().innerTest() println(demo2) // 內部類(lèi)可以引用外部類(lèi)的成員,例如:成員屬性}
為了消除歧義,要訪(fǎng)問(wèn)來(lái)自外部作用域的this,我們使用this@label,其中@label是一個(gè)代指this來(lái)源的標簽。
匿名內部類(lèi)
使用對象表達式來(lái)創(chuàng )建匿名內部類(lèi):
class Test { var v = "成員屬性"
fun setInterFace(test: TestInterFace) { test.test() }}
/** * 定義接口 */interface TestInterFace { fun test()}
fun main(args: Array<String>) { var test = Test()
/** * 采用對象表達式來(lái)創(chuàng )建接口對象,即匿名內部類(lèi)的實(shí)例。 */ test.setInterFace(object : TestInterFace { override fun test() { println("對象表達式創(chuàng )建匿名內部類(lèi)的實(shí)例") } })}
廣告時(shí)間,休息一下
類(lèi)的修飾符
類(lèi)的修飾符包括classModifier和_accessModifier_:
· classModifier: 類(lèi)屬性修飾符,標示類(lèi)本身特性。
abstract // 抽象類(lèi)final // 類(lèi)不可繼承,默認屬性enum // 枚舉類(lèi)open // 類(lèi)可繼承,類(lèi)默認是final的annotation // 注解類(lèi)
· accessModifier: 訪(fǎng)問(wèn)權限修飾符
private // 僅在同一個(gè)文件中可見(jiàn)protected // 同一個(gè)文件中或子類(lèi)可見(jiàn)public // 所有調用的地方都可見(jiàn)internal // 同一個(gè)模塊中可見(jiàn)
實(shí)例
// 文件名:example.ktpackage foo// 在example.kt內可見(jiàn)private fun foo() {}// 該屬性隨處可見(jiàn)public var bar: Int = 5// 相同模塊內可見(jiàn)internal val baz = 6
弦律公眾平臺
謝謝觀(guān)賞
弦律
與您共享
弦樂(lè )旋律