本系列文章來學(xué)習(xí) Kotlin 和 Anko 插件 通過 Kotlin 開發(fā)一個 Android 項目笼呆。
Kotlin-Anko學(xué)習(xí)(1) Kotlin、Anko 介紹
Kotlin-Anko學(xué)習(xí)(2) Kotlin 語法基礎(chǔ)-基本類型
Kotlin-Anko學(xué)習(xí)(3) Kotlin 語法基礎(chǔ)-關(guān)鍵字 package旨别、Import诗赌、if、when秸弛、for铭若、while、return递览、break叼屠、continue
Kotlin-Anko學(xué)習(xí)(4) Kotlin語法-類、繼承非迹、抽象類
Kotlin-Anko學(xué)習(xí)(5) Kotlin語法-屬性环鲤、字段、接口
Kotlin 類
與Java相同憎兽,類由 class 關(guān)鍵字聲明冷离。
類聲明的組成
- 類名 TestKot:
class TestKot{...}
- 類頭 在類名后聲明屬性、指定其類型參數(shù)纯命、主構(gòu)造函數(shù)等西剥。 如
/**
* val 聲明不變的屬性,var聲明可變屬性
* firstName lastName 指定為String類型參數(shù) age為Int類型參數(shù)
* Person(...) 為主構(gòu)造函數(shù)亿汞,是類頭的一部分瞭空,如果沒有類沒有聲明構(gòu)造函數(shù)(primary、secondary都沒有),系統(tǒng)會默認(rèn)生成一個無慘構(gòu)造函數(shù)咆畏,與java類似南捂。
*/
class Person(val firstName: String, val lastName: String, var age: Int) {
// ...
}
- 類體 TestKot:
class TestKot{...}
大括號中為類體部分,如果類沒有類頭旧找,類體可以簡寫成class TestKot
溺健。類體中可以包含:
構(gòu)造函數(shù)、屬性初始化钮蛛、初始化塊
- Kotlin 中一個類可以包含一個主構(gòu)造函數(shù)和一個或多個次構(gòu)造函數(shù)鞭缭,用關(guān)鍵字constructor修飾
- Kotlin 中屬性初始化可以在類頭中,也可以在類體中寫魏颓,并且可以獲得主構(gòu)造函數(shù)的參數(shù)值
- Kotlin 中由于主構(gòu)造函數(shù)在類頭中岭辣,不能寫初始化代碼,所以Kotlin 通過一個關(guān)鍵字 init 來包含初始化代碼塊
- 主構(gòu)造函數(shù)(primary constructor ):
如果主構(gòu)造函數(shù)沒有任何注解或者可見性修飾符甸饱,可以省略這個 constructor 關(guān)鍵字沦童。如下:
//省略constructor的寫法,默認(rèn)為public
class TestKot(name: String) {……}
//包含注解
class TestKot public @Inject constructor(name: String) { …… }
//聲明主構(gòu)造函數(shù)為私有
class TestKot private constructor(name: String){......}
- 次構(gòu)造函數(shù)(secondary constructor ):
如果類有一個主構(gòu)造函數(shù)柜候,每個次構(gòu)造函數(shù)需要委托給主構(gòu)造函數(shù)搞动, 可以直接委托或者通過別的次構(gòu)造函數(shù)間接委托。委托到同一個類的另一個構(gòu)造函數(shù)用 this 關(guān)鍵字渣刷。
class Constructors(name:String) {
//第一個屬性初始化
val firstProperty = "First property: $name".also(::println)
//第一個init初始化塊
init {
println("Init first")
}
//次構(gòu)造函數(shù)委托給次構(gòu)造函數(shù)(間接)
constructor(name:String,age: Int,weight:Int):this(name,age) {
println("Constructor name = $name age = $age weight = $weight kg")
}
//次構(gòu)造函數(shù)委托給主構(gòu)造函數(shù)(直接)
constructor(name:String,age: Int):this(name) {
println("Constructor name = $name age = $age")
}
//第二個init初始化塊
init {
println("Init second")
}
//第二個屬性初始化
val secondProperty = "Second property: $name ".also(::println)
}
fun main(args: Array<String>) {
Constructors("張三")
print("--------------------------------------------\n")
Constructors("李四",16)
print("--------------------------------------------\n")
Constructors("王五",17,55)
}
運行結(jié)果:
First property: 張三
Init first
Init second
Second property: 張三
--------------------------------------------
First property: 李四
Init first
Init second
Second property: 李四
Constructor name = 李四 age = 16
--------------------------------------------
First property: 王五
Init first
Init second
Second property: 王五
Constructor name = 王五 age = 17
Constructor name = 王五 age = 17 weight = 55 kg
從以上代碼可以看出,屬性初始化和init初始化代碼塊是按順序執(zhí)行的矗烛,次構(gòu)造函數(shù)式在屬性初始化和init執(zhí)行完才開始的辅柴,所以可以把屬性初始化和init初始化代碼塊當(dāng)做主構(gòu)造函數(shù)體來理解。這樣構(gòu)造函數(shù)部分就講完了瞭吃。
類的實例化
Kotlin 中類的實例化就像普通函數(shù)一樣直接調(diào)用碌嘀,如上面的main函數(shù)中的示例,不需要new與java不同歪架,也沒有new關(guān)鍵字股冗。
Kotlin 繼承
與java不同,kotlin中類的繼承在類頭后加冒號: “Current類:Base類”
Any 超類
Kotlin 中所有類都有一個共同的超類 Any,不同于java的Object類和蚪,只有三個方法代碼如下:
public open class Any {
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
}
open關(guān)鍵字
open 標(biāo)注與 Java 中 final 相反止状,它允許其他類從這個類繼承。默認(rèn)情況下攒霹,在 Kotlin 中所有的類都是 final怯疤,抽象類不需要用open修飾。
//超類型
open class Base(p: Int)
//基類
class Current(p: Int) : Base(p)
上面代碼可以看出超類有構(gòu)造函數(shù)的話催束,其基類可以(并且必須) 用(基類型的)主構(gòu)造函數(shù)參數(shù)就地初始化集峦。
super 關(guān)鍵字
super與java類似,用于超類屬性、方法的實現(xiàn)塔淤,這里我們看派生類如何初始化超類的構(gòu)造函數(shù)
//超類型
open class Base {
constructor(name:String ,age:Int)
constructor(name: String,age: Int,weight:Int):this(name,age)
}
//基類 示例1
class Current(name: String, age: Int) : Base(name, age) {
//constructor(name: String,age: Int,weight:Int):super(name,age,weight)//報錯
}
//基類 示例2
class Current(name: String,age: Int,weight:Int) : Base(name,age,weight) {
//constructor(name: String,age: Int):super(name,age)//報錯
}
//基類 示例3
class Current : Base {
constructor(name: String,age: Int):super(name,age)
constructor(name: String,age: Int,weight:Int):super(name,age,weight)
}
以上代碼可以看出摘昌,超類沒有主構(gòu)造函數(shù),基類可以通過主構(gòu)造函數(shù)初始化超類的任意一個次構(gòu)造函數(shù)高蜂,在此基礎(chǔ)上聪黎,基類的次構(gòu)造函數(shù)不能通過super來初始化超類的其他次構(gòu)造函數(shù),如示例1,2妨马;基類也可以不聲明主構(gòu)造函數(shù)挺举,通過次構(gòu)造函數(shù)super來初始化對應(yīng)的次構(gòu)造函數(shù),如示例3烘跺。
override 關(guān)鍵字
Kotlin 中 override 只能修飾父類顯式標(biāo)注可覆蓋的成員(開放open)湘纵,父類中沒有顯式標(biāo)注的成員在其子類中不允許定義相同簽名的函數(shù)。
覆蓋方法
open class Base {
open fun v() {}
fun nv() {}
}
//示例1
class Bar1() : Base() {
override fun v() {}
// override fun nv(){}//error:'nv' in 'Base' is final and cannot be overridden
// fun nv(){}//error: 'nv' hides member of supertype 'Base' and needs 'override' modifier
}
//示例2
class Foo {
// open val x: Int get() { return 1 } //Warning:'open' has no effect in a final class
//示例3
open class Bar2() : Base() {
final override fun v() {}
}
示例1:說明父類的顯式標(biāo)注方法滤淳,在子類中可以通過override修飾的相同方法覆蓋梧喷,對于沒有開放的函數(shù), 不論加不加 override脖咐,都不允許定義與父類相同簽名的方法铺敌。
示例2:說明在一個 final 類中(沒有用 open 標(biāo)注的類),open修飾的方法沒有用
示例3:說明子類重寫的方法如果想在屁擅,子類的子類中不被繼續(xù)重寫偿凭,可以在override前用final 進行修飾。
覆蓋屬性
open class Foo {
open val x: Int =1
open val y: Int get(){return 5}
}
示例1
class Bar1 : Foo() {
override val x: Int = 2
init{
print("x= $x \n")
print("y= $y \n")
}
}
示例2
class Bar2 : Foo(){
override var x : Int = 3
init{
print("x= $x")
}
}
fun main(args: Array<String>) {
Bar1()
Bar2()
}
輸出結(jié)果:
x= 2
x= 5
x= 3
重寫屬性的規(guī)則也是通過override進行修飾派歌,并且派生類的重寫的屬性必須兼容超類的屬性弯囊,示例1可以看出
聲明的屬性由具有初始化器的屬性或者具有 getter 方法的屬性覆蓋 ,示例2可以看出用一個 var 屬性覆蓋一個 val 屬性是可以的胶果,本質(zhì)上var的get方法覆蓋了val的get方法匾嘱,并且在派生類額外生成一個set方法。
調(diào)用超類的實現(xiàn)
調(diào)用超類中的方法與java相同早抠,通過super關(guān)鍵字修飾霎烙,屬性屬kotlin獨有的。
open class Foo {
open fun f() { println("Foo.f()") }
open val x: Int get() = 1
}
class Bar : Foo() {
override fun f() {
super.f()
println("Bar.f()")
}
override val x: Int get() = super.x + 1
init{
println("x= $x")
}
}
fun main(args: Array<String>) {
Bar().f()
}
輸出結(jié)果:
x= 2
Foo.f()
Bar.f()
以上就是派生類中調(diào)用超類具體的實現(xiàn)蕊连,跟java相似悬垃。
覆蓋規(guī)則
既有繼承又有接口的實現(xiàn)
Kotlin 中一個類從它的直接超類繼承相同成員的多個實現(xiàn), 它必須覆蓋這個成員并提供其自己的實現(xiàn)咪奖。 為了表示采用從哪個超類型繼承的實現(xiàn)盗忱,我們使用由尖括號中超類型名限定的 super,如 super<Base>
open class A {
open fun f() { println("A") }
fun a() { println("a") }
}
interface B {
fun f() { println("B") } // 接口成員默認(rèn)就是“open”的
fun b() { println("b") }
}
class C() : A(), B {
init{
println("C()")
}
// 編譯器要求覆蓋 f():
override fun f() {
super<A>.f() // 調(diào)用 A.f()
super<B>.f() // 調(diào)用 B.f()
}
}
fun main(args: Array<String>) {
C().f()
}
輸出結(jié)果:
C()
A
B
首先在派生類既有繼承又有接口羊赵,并且超類趟佃、接口中存在相同的方法扇谣,派生類必須重寫該方法,并且通過
surper<A>.f() 尖括號中表明實現(xiàn)哪個父類的方法闲昭,并且這里說明一下kotlin中接口中的方法默認(rèn)是open的罐寨。
內(nèi)部類中訪問外部類超類的實現(xiàn)
open class Foo {
open fun f() { println("Foo.f()") }
open val x: Int get() = 1
}
class Bar : Foo() {
override fun f() { println("Bar.f()")}
override val x: Int get() = 0
inner class Baz {
fun g() {
super@Bar.f() // 調(diào)用 Foo 實現(xiàn)的 f()
println(super@Bar.x) // 使用 Foo 實現(xiàn)的 x 的 getter
}
}
}
fun main(args: Array<String>) {
Bar().Baz().g()
}
輸出結(jié)果:
Foo.f()
1
內(nèi)部類需要實現(xiàn)外部類的屬性和方法,我們super@Outer().f()序矩、super@Outer.x來實現(xiàn)鸯绿。
抽象類
kotlin 中抽象類和java相同,通過 abstract 關(guān)鍵字來修飾簸淀,抽象類中可以包含抽象的方法或?qū)傩浴?/p>
open class Base {
open fun f() {}
}
abstract class Bar : Base() {
abstract val x :Int
override abstract fun f()
open fun g(){println("Bar.g()")}
}
class Child() :Bar(){
override var x: Int =2
override fun g() {
super.g()
println("Child g()")
}
override fun f() {
println("Child f()")
}
}
fun main(args: Array<String>) {
var child = Child()
child.g()
child.f()
}
輸出結(jié)果:
Bar.g()
Child g()
Child f()
上面的代碼可以看出瓶蝴,非抽象的超類可以派生出抽象類,并且開放的方法可以被重寫成抽象方法租幕,抽象方法是不允許有方法體的舷手,abstract 修飾的屬性或方法默認(rèn)是open的。這里與接口不同劲绪,接口中的方法默認(rèn)抽象的也是默認(rèn)開放的男窟,抽象類中的方法只有抽象的方法才是默認(rèn)open的。