自從實習(xí)結(jié)束后直到現(xiàn)在將近一年多的時間再也沒有用過kotlin殊者,
在今年五月份I/O大會上付秕,Google再次明確了Kotlin在Android開發(fā)中的地位豪治,并毫無懸念的將這個“后生晚輩”定為官方“首選”語言局齿,這使得Kotlin不得不被“傳統(tǒng)java
開發(fā)者”重視养铸,他們是時候改擁抱變化了,所以我們項目主要技術(shù)負(fù)責(zé)人現(xiàn)在勉強接受大家使用Kotlin開發(fā)意愿(實在不容易~)戒悠。
對荒廢了一年的語言現(xiàn)在重新拾起感覺又熟悉又陌生熬荆,但內(nèi)心還是很興奮的,最近準(zhǔn)備系統(tǒng)地整理一下Kotlin相關(guān)內(nèi)容救崔, 希望能在短時間內(nèi)重新掌握惶看,今天主要重新梳理一下Kotlin基礎(chǔ)語法捏顺。
Kotlin語言介紹
Kotlin 是 JetBrains 在 2010 年推出的基于 JVM 的新編程語言六孵,是一種新的靜態(tài)類型編程語言。開發(fā)者稱幅骄,設(shè)計它的目的是避免 Java 語言編程中的一些難題劫窒。比如:在 Kotlin 中類型系統(tǒng)控制了空指針引用,可以有效避免 Java 中常見的NullPointException拆座。
相比于 Java主巍,Kotlin 有著更好的語法結(jié)構(gòu)冠息,安全性和開發(fā)工具支持。
Kotlin 中沒有基礎(chǔ)類型孕索,數(shù)組是定長的逛艰,泛型是安全的,即便運行時也是安全的搞旭。此外散怖,該語言支持閉包吼驶,還可通過內(nèi)聯(lián)進(jìn)行優(yōu)化涩澡。不過尘颓,它不支持檢查異常(Checked Exceptions)搁嗓,許多語言設(shè)計者認(rèn)為這是它的瑕疵布朦。不論如何请敦,重要的是 Java 和 Kotlin 之間的互操作性:Kotlin 可以調(diào)用 Java贸营,反之亦可贾漏。
Kotlin基于JVM的字節(jié)碼結(jié)構(gòu)(與java同屬靜態(tài)語言)惑申,Kotlin 應(yīng)用程序的運行速度與 Java 類似具伍。,但隨著 Kotlin 對內(nèi)聯(lián)函數(shù)的支持圈驼,使用 lambda 表達(dá)式的代碼通常比用 Java 寫的代碼運行得更快沿猜。
Kotlin 可與 Java 進(jìn)行 100% 的互操作,允許在 Kotlin 應(yīng)用程序中使用所有現(xiàn)有的 Android 庫 碗脊,這包括注解處理啼肩,Google官方也說過未來Android支持庫優(yōu)先支持Kotlin語言;
Kotlin屬于跨平臺編譯語言衙伶,除了可以進(jìn)行原聲開發(fā)外很好的支持Java服務(wù)端程序開發(fā)祈坠、Android應(yīng)用程序開發(fā)以及JavaScript開發(fā);
Kotlin安裝與配置這里省略(網(wǎng)上很多教程矢劲,AndroidStudio3.x版本默認(rèn)已支持Kotlin插件)
基礎(chǔ)數(shù)據(jù)類型
Kotlin 的基本數(shù)據(jù)類型包括 Byte赦拘、Short、Int芬沉、Float躺同、Long、Double 丸逸、Boolean,
在java中基本數(shù)據(jù)類型一共有八種分別包括整形:byte蹋艺、short、int黄刚、long捎谨,浮點型:float、double , 字符型 char
以及布爾型 boolean涛救,不同于 Java 的是畏邢,Kotlin 字符不屬于數(shù)值類型,是一個獨立的數(shù)據(jù)類型,
大家可以看出Kotlin的基本數(shù)據(jù)類型和java不一樣检吆,Kotlin 中其實沒有基礎(chǔ)數(shù)據(jù)類型舒萎,只有封裝的對象類型,你每定義的一個變量蹭沛,其實
Kotlin 幫你封裝了一個對象逆甜,這樣可以保證不會出現(xiàn)空指針,
對應(yīng)Java中八大基本數(shù)據(jù)類型的對象類型、java在使用基礎(chǔ)數(shù)據(jù)類型時候可以通過裝箱操作封裝成對象致板;
字符
和 Java 不一樣交煞,Kotlin 中的 Char 不能直接和數(shù)字操作(可以通過顯示轉(zhuǎn)換char.toInt()),Char 必需是單引號 ' 包含起來的斟或。比如普通字符 '0'素征,'a',字符字面值用單引號括起來: '1'萝挤。 特殊字符可以用反斜杠轉(zhuǎn)義御毅。 支持這幾個轉(zhuǎn)義序列:\t、 \b怜珍、\n端蛆、\r、\'酥泛、\"今豆、\\ 和 \$。
當(dāng)需要可空引用時柔袁,像數(shù)字呆躲、字符會被裝箱。裝箱操作不會保留同一性捶索。
布爾
?????? 布爾用 Boolean 類型表示插掂,它有兩個值:true 和 false。當(dāng)需要可空引用布爾會被裝箱腥例。
數(shù)組
????? 數(shù)組用Array類實現(xiàn)辅甥,具有size屬性、get燎竖、setf方法璃弄,由于使用 [] 重載了 get 和 set 方法,所以我們可以通過下標(biāo)很方便的獲取或者設(shè)置數(shù)組對應(yīng)位置的值底瓣。java中素組具有l(wèi)ength屬性以及使用[]通過下標(biāo)方式訪問屬性谢揪;
數(shù)組的創(chuàng)建兩種方式:一種是使用函數(shù)arrayOf();另外一種是使用工廠函數(shù)捐凭。如下所示拨扶,我們分別是兩種方式創(chuàng)建了兩個數(shù)組:
fun main(args: Array<String>) {
? ? //[1,2,3]
? ? val a = arrayOf(1, 2, 3)
? ? //讀取數(shù)組內(nèi)容
? ? println(a[0])? ? // 輸出結(jié)果:1
? ? println(b[1])? ? // 輸出結(jié)果:2
}
val x: IntArray = intArrayOf(1, 2, 3
除了類Array,還有ByteArray, ShortArray, IntArray茁肠,LongArray等用來表示各個類型的數(shù)組患民,省去了裝箱操作,因此效率更高垦梆,其用法同Array一樣:
字符串
和java一樣,String屬于不可變的托猩,Kotlin可以通過[]很方便訪問對應(yīng)下標(biāo)字符印蓖,java中通過chatAt方法或者subString等方式獲取對應(yīng)字符,Kotlin中String支持遍歷形式訪問其中的字符京腥,這一點很使用赦肃;
for (c in str) {
? ? println(c)
}
另外Kotlin 支持三個引號 """ 擴(kuò)起來的字符串,支持多行字符串公浪,比如:
fun main(args: Array<String>) {
? ? val text = """多行字符串 多行字符串"""
? ? println(text)? // 輸出
}
String 可以通過 trim(),trimEnd(),trimStart(),trimMargin() 等方法來刪除多余的空白他宛。
類型轉(zhuǎn)換
java中可以通過隱式類型轉(zhuǎn)換,數(shù)值大的類型可以轉(zhuǎn)換成數(shù)據(jù)小的類型欠气,但是這樣往往會丟失精度厅各,在kotlin中由于不同的表示方式,較小類型并不是較大類型的子類型预柒,較小的類型不能隱式轉(zhuǎn)換為較大的類型队塘。
這意味著在不進(jìn)行顯式轉(zhuǎn)換的情況下我們不能把 Byte 型值賦給一個 Int 變量,
val b: Byte = 1 // OK, 字面值是靜態(tài)檢測的
val i: Int = b.toInt() // OK
每種數(shù)據(jù)類型都有下面的這些方法宜鸯,可以轉(zhuǎn)化為其它的類型:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
2. 變量人灼、常量聲明
定義只讀變量使用關(guān)鍵字val定義,只能為其賦值一次顾翼,
val a: Int = 1? // 立即賦值 (非空屬性必須在定義時候初始化投放,)
val b = 2? // 自動推斷出 `Int` 類型? (非空屬性必須在定義時候初始化,)
可重新賦值的變量使用var關(guān)鍵字:
var x = 5 // 自動推斷出 `Int` 類型 (非空屬性必須在定義時候初始化适贸,)
x += 1
Kotlin語法支持類型自動推斷灸芳,在聲明變量或者常量的時候可以不用指定其類型,編譯器在編譯時候會為我們指定其類型拜姿;
非空屬性必須在定義的時候初始化,kotlin提供了一種可以延遲初始化的方案,使用 lateinit 關(guān)鍵字描述屬性:
var count:Int?=null //可空屬性可以在后面賦值
count=5
lateinit var name : String 非空屬性使用延遲初始化
函數(shù)定義
函數(shù)定義使用fun關(guān)鍵字烙样,參數(shù)格式為 參數(shù):類型 ,最后函數(shù)返回值類型蕊肥,如下
fun sum( a:Int, b:Int):Int{return a+b}
亦可以函數(shù)表達(dá)式聲明函數(shù)
fun sum( a:Int, b:Int)=? a+b? // 自動類型推斷或者 fun sum(a:Int,b:Int):Int=a+b?
無返回值的函數(shù)定義(類似Java中的void):
fun printSum(a: Int, b: Int): Unit {
? ? print(a + b)
}
Unit返回類型可以省略:
public fun printSum(a: Int, b: Int) {
? ? print(a + b)
}
3. 可變長參數(shù)函數(shù)
可變長參數(shù)用vararg關(guān)鍵字進(jìn)行修飾:
fun print(vararg v:Int){?
? ? ? ? for(a in v){?
? ? ? ? ? ? println("$a")
? ? ? ? }
}
4. 字符串模板
$ 表示一個變量名或者變量值
$varName 表示變量值
${varName.fun()} 表示變量的方法返回值:
var a = 1
// 模板中的簡單名稱:
val s1 = "a is $a"
a = 2
// 模板中的任意表達(dá)式:
val s2 = "${s1.replace("is", "was")}, but now is $a"
// 運行結(jié)果:a was 1, but now is 2
5. 條件表達(dá)式
看一個常用if表達(dá)式:
fun value(a:Int, b :Int):Int {
? if(a>b) {
? ? return a+b?
? }else{?
? ? return a-b? ?
? }
}
通過條件表達(dá)式可以:
fun value(a:Int,b:Int)=if(a>b) a+b else a-b
6. NULL檢查機(jī)制
Kotlin的空安全設(shè)計對于聲明可為空的參數(shù)谒获,在使用時要進(jìn)行空判斷處理蛤肌,有兩種處理方式,字段后加!!像Java一樣拋出空異常批狱,另一種字段后加?可不做處理返回值為 null或配合?:做空判斷處理
//類型后面加?表示可為空
var age: String? = "23"
//拋出空指針異常
val ages = age!!.toInt()
//不做處理返回 null
val ages1 = age?.toInt()
//age為空返回-1
val ages2 = age?.toInt() ?: -1
當(dāng)一個引用可能為 null 值時, 對應(yīng)的類型聲明必須明確地標(biāo)記為可為 null裸准。
Kotlin 的NULL機(jī)制旨在消除來自代碼空引用的危險,這在java語言中屬于最常見的陷阱之一赔硫,也就是訪問空引用的成員會導(dǎo)致空引用異常炒俱。經(jīng)常會拋出NullPointerException或簡稱NPE。
在Kotlin中經(jīng)常會有一些鏈?zhǔn)秸{(diào)用用法爪膊,安全調(diào)用在鏈?zhǔn)秸{(diào)用中很有用权悟,如:person?.class?.countName 如果調(diào)用鏈中任何一個屬性值出現(xiàn)null情況,調(diào)用鏈會直接返回null推盛,
后面屬性不會出現(xiàn)NPE異常峦阁。
如果相對非null值執(zhí)行某個操作,可以結(jié)合let操作符一起使用:
val listWithNulls: List<String?> = listOf("Kotlin", null)? ? ?
for (item in listWithNulls) {
? ? ? ? ? item?.let {?
? ? ? ? ? println(it) // 輸出 Kotlin 并忽略 null?
? ? }
}
同樣安全調(diào)用也可以出現(xiàn)在賦值的左側(cè)耘成,如果調(diào)用鏈中的任何一個接收者為空都會跳過賦值拇派,而右側(cè)的表達(dá)式根本不會求值:
person?.class?.mathTeacher = TeachManager.getTeacher()
7. Elvis 操作符
當(dāng)我們有一個可空的引用r時,我們可以說“如果r非空凿跳,我使用它件豌;否則使用某個非空的值x”:
val l: Int = if (b != null) b.length else -1
或者
fun value(a:Int,b:Int)=if(a>b) a+b else a-b
//條件表達(dá)式用法
除了完整的 if-表達(dá)式,這還可以通過 Elvis 操作符表達(dá)控嗜,寫作?::
val l = b?.length ?: -1
如果?:左側(cè)表達(dá)式非空茧彤,elvis 操作符就返回其左側(cè)表達(dá)式,否則返回右側(cè)表達(dá)式疆栏。請注意曾掂,當(dāng)且僅當(dāng)左側(cè)為空時,才會對右側(cè)表達(dá)式求值壁顶。
這種Elvis用法類似java語言中三元操作符珠洗;
public int value(int a,int b){return a>b?a+b :a-b}
等同于:
fun value(a:Int,b:Int)=if(a>b) a+b else a-b
val l = b?.length ?: -1
等同于:
final int l= b!=null? b.length : -1
8. 類型檢測及類型轉(zhuǎn)換
如果對象不是目標(biāo)類型,那么常規(guī)類型轉(zhuǎn)換可能會導(dǎo)致ClassCastException若专,
val aInt: Int? = a as Int
為了避免類型轉(zhuǎn)成異常许蓖,另一個選擇是使用安全的類型轉(zhuǎn)換,如果嘗試轉(zhuǎn)換不成功則返回 null:
val aInt: Int? = a as? Int
或者
val sInt :Int?= if(a is Int) a as Int else null
或者
val sInt :Int?= if(a is Int) a else null
is 運算符檢測一個表達(dá)式是否某類型的一個實例(類似于Java中的instanceof關(guān)鍵字), 如果一個不可變的局部變量或?qū)傩砸呀?jīng)判斷出為某類型调衰,那么檢測后的分支中可以直接當(dāng)作該類型使用膊爪,無需顯式轉(zhuǎn)換:
fun getStringLength(obj: Any): Int? {
? if (obj is String) {
? ? // 做過類型判斷以后,obj會被系統(tǒng)自動轉(zhuǎn)換為String類型
? ? return obj.length
? }
? // 這里的obj仍然是Any類型的引用
? return null
}
或者
fun getStringLength(obj: Any): Int? {
? if (obj !is String)
? ? return null
? // 在這個分支中, `obj` 的類型會被自動轉(zhuǎn)換為 `String`
? return obj.length
}
9. 可空類型的集合
如果你有一個可空類型元素的集合嚎莉,并且想要過濾掉空元素米酬,你可以使用filterNotNull來實現(xiàn):
val listOf = listOf<Int?>(1, 2, null, 4)
val intList: List<Int> = listOf.filterNotNull()
10. 區(qū)間
區(qū)間表達(dá)式由具有操作符形式 .. 的 rangeTo 函數(shù)輔以 in 和 !in 形成。
使用 in 運算符來檢測某個數(shù)字是否在指定區(qū)間內(nèi):
for (i in 1..4) print(i) // 輸出“1234”
for (i in 4..1) print(i) // 什么都不輸出
if (i in 1..10) { // 等同于 1 <= i && i <= 10
? ? println(i)
}
// 使用 step 指定步長
for (i in 1..4 step 2) print(i) // 輸出“13”
for (i in 4 downTo 1 step 2) print(i) // 輸出“42”
// 使用 until 函數(shù)排除結(jié)束元素
for (i in 1 until 10) {? // i in [1, 10) 排除了 10
? ? println(i)
}
val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
? ? println("-1 is out of range")
}
if (list.size !in list.indices) {
? ? println("list size is out of valid list indices range, too")
}
11. 使用 for 循環(huán)
for 循環(huán)可以對任何提供迭代器(iterator)的對象進(jìn)行遍歷趋箩,語法如下:
同樣赃额,kotlin的for循環(huán)中使用的也是in操作符加派,
val items = listOf("dog", "cat", "pig")
for (item in items) {
println(item)
}
或者通過索引
val items = listOf("apple", "banana", "kiwifruit")
for (index in items.indices) {
? ? println("item at $index is ${items[index]}")
}
val array=arrayOf("a","b","c")
for (i in array.indices) {
? ? print(array[i])
}
12. 使用 when 表達(dá)式
when 將它的參數(shù)和所有的分支條件順序比較,直到某個分支滿足條件跳芳,
when 既可以被當(dāng)做表達(dá)式使用也可以被當(dāng)做語句使用芍锦。如果它被當(dāng)做表達(dá)式,符合條件的分支的值就是整個表達(dá)式的值筛严,如果當(dāng)做語句使用醉旦, 則忽略個別分支的值饶米。
when 類似java語言的 switch 操作符桨啃。其最簡單的形式如下:
fun describe(obj: Any): String =
when (obj) {
1? ? ? ? ? -> "One"
"Hello"? ? -> "Greeting"
is Long? ? -> "Long"
!is String -> "Not a string"
else? ? ? -> "Unknown"
}
在Kotlin中Any類是所有類的超類,類似java中的Object檬输;
在 when 中照瘾,else 同 switch 的 default。如果其他分支都不滿足條件將會求值 else 分支丧慈。如果很多分支需要用相同的方式處理析命,則可以把多個分支條件放在一起,用逗號分隔逃默。
when 也可以用來取代 if-else if鏈鹃愤。 如果不提供參數(shù),所有的分支條件都是簡單的布爾表達(dá)式完域,而當(dāng)一個分支的條件為真時則執(zhí)行該分支:
when {
? ? x.isOdd() -> print("x is odd")
? ? x.isEven() -> print("x is even")
? ? else -> print("x is funny")
}
13. 使用 lambda 表達(dá)式來過濾(filter)與映射(map)集合
val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits.filter { it.startsWith("a") }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { println(it) }
結(jié)果:APPLE AVOCADO
14. 類以及構(gòu)造器
類的修飾符包括 classModifier 和_accessModifier_:
classModifier: 類屬性修飾符软吐,標(biāo)示類本身特性。
abstract? ? // 抽象類?
final? ? ? // 類不可繼承吟税,默認(rèn)屬性
enum? ? ? ? // 枚舉類
open? ? ? ? // 類可繼承凹耙,類默認(rèn)是final的
annotation? // 注解類
accessModifier: 訪問權(quán)限修飾符
private? ? // 僅在同一個文件中可見
protected? // 同一個文件中或子類可見
public? ? // 所有調(diào)用的地方都可見
internal? // 同一個模塊中可見
主構(gòu)造器(主構(gòu)造函數(shù))
Koltin 中的類可以有一個 主構(gòu)造器,以及一個或多個次構(gòu)造器肠仪,主構(gòu)造器是類頭部的一部分肖抱,位于類名稱之后:
class Person constructor(firstName: String) {}
如果主構(gòu)造器沒有任何注解,也沒有任何可見度修飾符异旧,那么constructor關(guān)鍵字可以省略意述。
class Person(firstName: String) {
}
主構(gòu)造器中不能包含任何代碼,初始化代碼可以放在初始化代碼段中吮蛹,初始化代碼段使用 init 關(guān)鍵字作為前綴欲险。
class Person constructor(firstName: String) {
? ? init {
? ? ? firstName="Scus"? //主構(gòu)造器的參數(shù)可以在初始化代碼段中使用
? ? }
}
另外可以通過主構(gòu)造器來定義屬性并初始化屬性值(可以是var或val):
class People(val firstName: String, val lastName: String) {
? ? //...
}
如果一個非抽象類沒有聲明構(gòu)造函數(shù)(主構(gòu)造函數(shù)或次構(gòu)造函數(shù)),它會產(chǎn)生一個沒有參數(shù)的構(gòu)造函數(shù)匹涮。構(gòu)造函數(shù)是 public 天试。如果你不想你的類有公共的構(gòu)造函數(shù),你就得聲明一個空的主構(gòu)造函數(shù):
class DontCreateMe private constructor () {
}
次構(gòu)造函數(shù)
類也可以有二級構(gòu)造函數(shù)然低,需要加前綴 constructor:
class Person(val name: String) {
? ? constructor (name: String, age:Int) : this(name) {
? ? ? ? // 初始化...
? ? }
}
抽象類
抽象是面向?qū)ο缶幊痰奶卣髦幌裁浚惐旧砦裉疲蝾愔械牟糠殖蓡T,都可以聲明為abstract的带兜。抽象成員在類中不存在具體的實現(xiàn)枫笛。
注意:無需對抽象類或抽象成員標(biāo)注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
? ? }
}
匿名內(nèi)部類
使用對象表達(dá)式來創(chuàng)建匿名內(nèi)部類:
? ? test.setInterFace(object : TestInterFace {
? ? ? ? override fun test() {
? ? ? ? ? ? println("對象表達(dá)式創(chuàng)建匿名內(nèi)部類的實例")
? ? ? ? }
? ? })
內(nèi)部類
內(nèi)部類使用 inner 關(guān)鍵字來表示刑巧。
內(nèi)部類會帶有一個對外部類的對象的引用,所以內(nèi)部類可以訪問外部類成員屬性和成員函數(shù)无畔。
class Outer {
? ? private val bar: Int = 1
? ? var v = "成員屬性"
? ? /**嵌套內(nèi)部類**/
? ? inner class Inner {
? ? ? ? fun foo() = bar? // 訪問外部類成員
? ? ? ? fun innerTest() {
? ? ? ? ? ? var o = this@Outer //獲取外部類的成員變量
? ? ? ? ? ? println("內(nèi)部類可以引用外部類的成員啊楚,例如:" + o.v)
? ? ? ? }
? ? }
}
數(shù)據(jù)類
Kotlin 可以創(chuàng)建一個只包含數(shù)據(jù)的類,關(guān)鍵字為 data:
data class User(val name: String, val age: Int)
密封類
密封類用來表示受限的類繼承結(jié)構(gòu):當(dāng)一個值為有限幾種的類型, 而不能有任何其他類型時浑彰。在某種意義上恭理,他們是枚舉類的擴(kuò)展:枚舉類型的值集合 也是受限的,但每個枚舉常量只存在一個實例郭变,而密封類 的一個子類可以有可包含狀態(tài)的多個實例颜价。
聲明一個密封類,使用 sealed 修飾類诉濒,密封類可以有子類周伦,但是所有的子類都必須要內(nèi)嵌在密封類中。
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
? ? is Const -> expr.number
? ? is Sum -> eval(expr.e1) + eval(expr.e2)
? ? NotANumber -> Double.NaN
}
15. 接口
Kotlin 接口與 Java 8 類似未荒,使用 interface 關(guān)鍵字定義接口专挪,允許方法有默認(rèn)實現(xiàn):
interface MyInterface {
? ? fun bar() // 未實現(xiàn)? ?
? ? fun foo() {//已實現(xiàn)? ? ?
? ? ? ? println("foo")?
? ? }
}
一個類或者對象可以實現(xiàn)一個或多個接口。
class Child : MyInterface {
? ? override fun bar() {
? ? ? ? // 方法體
? ? }
? ? override fun fool() {
? ? ? ? // 方法體
? ? }
}
接口中的屬性只能是抽象的茄猫,不允許初始化值狈蚤,接口不會保存屬性值,實現(xiàn)接口時划纽,必須重寫屬性:
interface MyInterface{
? ? var name:String //name 屬性, 抽象的
}
class MyImpl:MyInterface{
? ? override var name: String = "runoob" //重寫屬性
}
16. Kotlin 繼承
kotlin 中所有類都繼承該 Any 類脆侮,它是所有類的超類,對于沒有超類型聲明的類是默認(rèn)超類:
如果一個類要被繼承勇劣,可以使用 open 關(guān)鍵字進(jìn)行修飾靖避。
open class Base(p: Int)? ? ? ? ? // 定義基類
class Derived(p: Int) : Base(p)
注意:Any 不是 java.lang.Object。
如果子類有主構(gòu)造函數(shù)比默, 則基類必須在主構(gòu)造函數(shù)中立即初始化幻捏。
open class Person(var name : String, var age : Int){// 基類
}
class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {
}
如果子類沒有主構(gòu)造函數(shù),則必須在每一個二級構(gòu)造函數(shù)中用 super 關(guān)鍵字初始化基類命咐,或者在代理另一個構(gòu)造函數(shù)篡九。初始化基類時,可以調(diào)用基類的不同構(gòu)造方法醋奠。
class Student : Person {
? ? constructor(ctx: Context) : super(ctx) {
? ? }
? ? constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
? ? }
}
在基類中榛臼,使用fun聲明函數(shù)時伊佃,此函數(shù)默認(rèn)為final修飾,不能被子類重寫沛善。如果允許子類重寫該函數(shù)航揉,那么就要手動添加 open 修飾它,。
17.對象聲明
Kotlin 使用 object 關(guān)鍵字來聲明一個對象金刁。
Kotlin 中我們可以方便的通過對象聲明來獲得一個單例帅涂。
object DataProviderManager {
? ? fun registerDataProvider(provider: DataProvider) {
? ? ? ? // ……
? ? }
? ? val allDataProviders: Collection<DataProvider>
? ? ? ? get() = // ……
}
引用該對象,我們直接使用其名稱即可:
DataProviderManager.registerDataProvider(……)
當(dāng)然你也可以定義一個變量來獲取獲取這個對象尤蛮,當(dāng)你定義兩個不同的變量來獲取這個對象時媳友,你會發(fā)現(xiàn)你并不能得到兩個不同的變量,也就是說通過這種方式抵屿,我們獲得一個單例:如
object Site {
? ? var url:String = ""
? ? val name: String = "菜鳥教程"
}
fun main(args: Array<String>) {
? ? var s1 =? Site
? ? var s2 = Site
? ? s1.url = "www.runoob.com"
? ? println(s1.url)
? ? println(s2.url)
}
// 輸出結(jié)果都為"www.runnoob.com"
18.伴生對象
類內(nèi)部的對象聲明可以用 companion 關(guān)鍵字標(biāo)記庆锦,這樣它就與外部類關(guān)聯(lián)在一起捅位,我們就可以直接通過外部類訪問到對象的內(nèi)部元素轧葛。
class MyClass {
? ? companion object Factory {
? ? ? ? fun create(): MyClass = MyClass()
? ? }
}
val instance = MyClass.create()? // 訪問到對象的內(nèi)部元素
我們可以省略掉該對象的對象名,然后使用 Companion 替代需要聲明的對象名:
class MyClass {
? ? companion object {
? ? }
}
注意:一個類里面只能聲明一個內(nèi)部關(guān)聯(lián)對象艇搀,即關(guān)鍵字 companion 只能使用一次尿扯。
伴生對象的成員看起來像java的靜態(tài)成員,但在運行時他們?nèi)匀皇钦鎸崒ο蟮膶嵗蓡T焰雕。