創(chuàng)建對象
Java | Kotlin |
---|---|
A a = new A() | var a = A() |
類型聲明
Java | Kotlin |
---|---|
String s | var s : String |
final String s | val s : String |
class A extends B | class A : B |
class A implements B | class A : B |
字符串模板
Java | Kotlin |
---|---|
String.format("%s今年%d歲", name, age); | "${name}今年${age}歲" |
方法
Java | Kotlin |
---|---|
String text(int para1,String para2) { ... } | fun test(para1: Int, para2: String): String { ... } |
Kotlin 特性 : 函數(shù)參數(shù)默認(rèn)值和可變參數(shù)
對Kotlin函數(shù)中的某個參數(shù)可以用“=”號指定其默認(rèn)值,調(diào)用函數(shù)方法時可不不傳這個參數(shù)冷离,但其他參數(shù)需要用“=”號指定。下文例子中沒有傳遞參數(shù)para2灼芭,其實際值為默認(rèn)值"para2"
fun test(para1: Int, para2: String = "para2", para3: String): String { ... }
test(22, para3 = "hello")
可變參數(shù)值的話,需要用關(guān)鍵字vararg來定義。這里需要注意的是,一個函數(shù)僅能有一個可變參數(shù)叶圃。該可變參數(shù)不一定得是最后一個參數(shù),但當(dāng)這種情況下時践图,調(diào)用該方法掺冠,需要給其他未指明具體值的參數(shù)傳值。
fun test(vararg para1: String, para2: String): String { ... }
test("para1", "para4", "para5", para2 = "hello")
構(gòu)造函數(shù)
Kotlin
類的構(gòu)造函數(shù)分為==primary constructor==和==secondary constructor==码党,前者只能有一個德崭,而后者可以有多個悍及。如果兩者都未指定,則默認(rèn)為無參數(shù)的primary constructor接癌。
primary constructor是屬于類頭的一部分心赶,用constructor關(guān)鍵字定義,無可見性字段定義時可省略缺猛。初始化代碼需要單獨寫在init代碼塊中缨叫,==primary constructor的參數(shù)只能在init代碼塊和變量初始化時使用。==
secondary constructor也是用constructor關(guān)鍵字定義荔燎,必須要直接或間接代理primary constructor耻姥。
class Student(name: String) { // primary constructor
var mName: String = name
init {
println("Student is called " + name)
}
constructor(age: Int, name: String):this(name) {
println("Student is ${age} years old")
}
}
繼承
Java | Kotlin |
---|---|
Java中類默認(rèn)可被繼承,只有被final關(guān)鍵字修飾的類才不能被繼承 | 僅有被open修飾的類才可以被繼承 |
單例
Java | Kotlin |
---|---|
用代碼實現(xiàn)單例 | 用關(guān)鍵詞 object 定義單例類 |
object Shop { // 不能有 primary constructor的參數(shù) ,
fun buySomething() {
println("Bought it")
}
}
Shop.buysomething() // 調(diào)用
靜態(tài)標(biāo)識與調(diào)用
Java | Kotlin |
---|---|
static標(biāo)識一個類里的靜態(tài)屬性或方法 | 用companion修飾單例類object有咨,來實現(xiàn)靜態(tài)屬性或方法功能 |
class Mall(name: String) {
// companion 伴隨對象 在一個類中 [唯一]
companion object Shop { // 與 java 不同,Kotlin 所有靜態(tài)方法和字段都寫在內(nèi)部單例類中
val SHOP_NAME: String = "McDonald" // 等同于Java中寫public static String
fun buySomething() { // 等同于Java中寫public static void
println("Bought it")
}
}
}
Mall.buySomething() // 可直接調(diào)用,
if-else語句
Kotlin
Kotlin中的if-else語句與Java一致琐簇,結(jié)構(gòu)上都是if (條件A) { 條件A為真要執(zhí)行的操作 } else { 條件A為假要執(zhí)行的操作 }
這里主要要介紹的是Kotlin的一個特點,即if-else語句可以作為一個邏輯表達(dá)式使用座享。不僅如此婉商,邏輯表達(dá)式還可以以代碼塊的形式出現(xiàn),代碼塊最后的表達(dá)式作為該塊的值渣叛。
// 邏輯表達(dá)式的使用
fun maxNum(x: Int, y: Int): Int {
var max = if (x > y) x else y
return max
}
// 代碼塊形式的邏輯表達(dá)式
fun maxNumPlus(x: Int, y: Int): Int {
var max = if (x > y) {
println("Max number is x")
x
} else {
println("Max number is y")
y
}
return max
}
when語句
Java | Kotlin |
---|---|
switch-case語句 | 有多種形式的條件表達(dá)丈秩。與if-else一樣Kotlin中的when也可以作為邏輯表達(dá)式使用 |
// 邏輯表達(dá)式的使用
fun judge(obj: Any) { // Any 相當(dāng)于 java中 Object
when (obj) {
1 -> println("是數(shù)字1")
-1, 0 -> println("是數(shù)字0或-1")
in 1..10 -> println("是不大于10的正整數(shù)")
"abc" -> println("是字符串a(chǎn)bc")
is Double -> println("類型是雙精度浮點數(shù)")
else -> println("其他操作")
}
}
標(biāo)簽
Kotlin中可以對任意表達(dá)式進(jìn)行標(biāo)簽標(biāo)記,形式為abc@淳衙,xyz@蘑秽。而這些標(biāo)簽,可以搭配return箫攀、break肠牲、continue等跳轉(zhuǎn)行為來使用。
fun labelTest() {
la@ for (i in 1..10) {
println("outer index " + i)
for (j in 1..10) {
println("inner index " + j )
if ( inner % 2 == 0) {
break@la
}
}
}
}
符號“?”
Kotlin中靴跛,當(dāng)我們定義一個變量時缀雳,其默認(rèn)就是非空類型。如果你直接嘗試給他賦值為null汤求,編譯器會直接報錯俏险。Kotlin中將符號“?”定義為安全調(diào)用操作符。變量類型后面跟?號定義扬绪,表明這是一個可空類型竖独。同樣的,在調(diào)用子屬性和方法時挤牛,也可以用字符?進(jìn)行安全調(diào)用莹痢。Kotlin的編譯器會在寫代碼時就檢查非空情況,因此下文例子中,當(dāng)s2有前置條件判斷為非空后竞膳,即便其本身是可空類型航瞭,也可以安全調(diào)用子屬性或方法。對于ifelse結(jié)構(gòu)的邏輯坦辟,Kotlin還提供了“?:”操作符刊侯,極大了簡化了代碼量又不失可讀性。Kotlin還提供“!!”雙感嘆號操作符來強制調(diào)用對象的屬性和方法锉走,無視其是否非空滨彻。這是一個挺危險的操作符,除非有特殊需求挪蹭,否則為了遠(yuǎn)離NPE亭饵,還是少用為妙。
var s1: String = "abc"
s1 = null // 這里編譯器會報錯
var s2: String? = "abc"
s2 = null // 編譯器不會報錯
var l1 = s1.length // 可正常編譯
var l2 = s2.length // 沒有做非空判斷梁厉,編譯器檢查報錯
if (s2 != null) s2.length // Java式的判空方案
s2?.length // Kotlin的安全調(diào)用操作符?辜羊。當(dāng)s2為null時,s2?.length也為null
if (s2 != null) s2.length else -1 // Java式判空的ifelse邏輯
s2?.length ?: -1 // Kotlin的elvis操作符
s2!!.length // 可能會導(dǎo)致NPE
擴展方法
擴展函數(shù)定義形式
在方法體內(nèi)是可以調(diào)用receiverType的對象中原本的方法
fun receiverType.functionName(params){
body // 注: 在方法體中使用 this ,那么 this 代表receiverType的對象(調(diào)用者)
}
- receiverType:表示函數(shù)的接收者词顾,也就是函數(shù)擴展的對象
- functionName:擴展函數(shù)的名稱
- params:擴展函數(shù)的參數(shù)八秃,可以為NULL
常見例子,其中 this 代表 context ,
fun Context.showLongToast(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
}
擴展方法位置
簡單說可以分為兩種: class 里面 ,class 外面
- 在一個類里面寫擴展方法,這時,在其他類直接調(diào)用是不生效的.
class B {
fun Student.doF(){
doFly() // Student 中原本就存在的方法
}
fun dow2(s:Student){
s.doF()
}
}
// 調(diào)用
B().dow2(Student())
在其他類調(diào)用 student.doF() 是不生效的 ,只有創(chuàng)建B對象調(diào)用dow2(s),擴展方法才能被調(diào)用
- 單獨新建一文件,或者寫在類的外面,可全部任意調(diào)用,如上面toast的例子,將擴展方法寫在單獨的文件中,在任意activity或者擁有Context的類中都能調(diào)用
/**
* 單獨一個類
*/
package ****
import android.content.Context
import android.widget.Toast
import java.util.*
// 擴展方法
fun Context.showLongToast(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
}
/**
* 一個類內(nèi) ,class 外
*/
fun Context.showLongToast(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
}
class Student(name: String) {
...
}
// 調(diào)用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
showLongToast("111")
}
}
伴隨對象的擴展
伴隨對象通過“類名.”形式調(diào)用伴隨對象。假如對伴隨對象聲明了擴展函數(shù)該怎么調(diào)用呢计技?其調(diào)用方式與伴隨對象一樣喜德,都是通過用類名限定符來調(diào)用。當(dāng)然垮媒,擴展屬性也是這樣的。(伴隨對象companion 的使用,請看上文)
fun Student.Shop.doW(){
println("伴隨對象的擴展函數(shù)")
}
//調(diào)用
Student.doW()
擴展函數(shù)名與原類中的函數(shù)名相同
相同時,調(diào)用時根據(jù)擴展函數(shù)寫的位置不同,會存在兩種情況
- 類外.setText()與 TextView 中的方法同名,但是在調(diào)用時,會全部提示出來
fun TextView.setText(msg: String) {
this.setText("擴展函數(shù)"+msg)
}
但是就傲嬌的不按提示調(diào)用,生寫一個一樣的方法時,這種情況下航棱,總是會優(yōu)先使用成員函數(shù),而不是擴展
open class Person {
fun doFly() {
println("親生的")
}
}
fun Person.doFly() {
println("后生的")
}
fun main(args: Array<String>) {
val person: Person = Person()
person.doFly()
}
// Log打印
親生的
- 類內(nèi),通過下面的例子可以得出結(jié)論,當(dāng)出現(xiàn)重名 doFly() 時,會默認(rèn)調(diào)用this(也就是 Person)中的方法
open class Person {
fun doFly() {
println("Person do fly")
}
}
class MyInfo {
fun doRun() {
println("MyInfo do run")
}
fun doFly() {
println("MyInfo do fly")
}
fun Person.doSwim() {
doFly() // 默認(rèn)調(diào)用this(也就是 Person)中的方法
doRun()
}
fun doSomething(person: Person) {
person.doSwim()
}
}
// 測試實例
fun main(args: Array<String>) {
val myInfo: MyInfo = MyInfo()
val person: Person = Person()
myInfo.doSomething(person)
}
// Log
Person do fly
MyInfo do run
但是如果也需要調(diào)用 MyInfo 中的doFly(),那么在
doFly()前面加上 "this@MyInfo. " 即可.
fun Person.doSwim() {
doFly()
this@MyInfo.doFly()
doRun()
}
// Log
Person do fly
MyInfo do fly
MyInfo do run
接收者可為NULL
調(diào)用者對象為空時,也可以調(diào)用
fun Person.doFly() {
if (null == this) {
println("null")
}
println("doFly")
}
擴展屬性
擴展屬性, 即 Extension Property , 即把某些函數(shù)添加為數(shù)據(jù), 使用”=”, 直接設(shè)置或使用睡雇。
val List.lastIndex: Int
get() = size - 1
注:
由于擴展屬性實際上不會向類添加新的成員, 因此無法讓一個擴展屬性擁有一個后端域變量. 所以,對于擴展屬性不允許存在初始化器. 擴展屬性的行為只能通過明確給定的取值方法與設(shè)值方法來定義,也就意味著擴展屬性只能被聲明為val而不能被聲明為var.如果強制聲明為var饮醇,即使進(jìn)行了初始化它抱,在運行也會報異常錯誤,提示該屬性沒有后端域變量朴艰。
數(shù)據(jù)類
Java中數(shù)據(jù)model观蓄,通常都是由多個屬性和對應(yīng)的getter、setter組成祠墅。當(dāng)有大量多屬性的數(shù)據(jù)類時侮穿,不僅這些類會因為大量的getter和setter方法而行數(shù)爆炸,也使整個工程方法數(shù)驟增毁嗦。Kotlin中也做了這層特性優(yōu)化亲茅,提供了數(shù)據(jù)類的簡單實現(xiàn)。不用再寫get和set方法,這些都由編譯器背后去做克锣,你得到的是一個清爽干凈的數(shù)據(jù)類茵肃。具體使用參考下面的例子。
data class Student (
var name: String,
var age: Int,
var hobby: String?,
var university: String = "NJU"
)
fun printInfo() {
var s: Student = Student("Ricky", 25, "playing Gwent")
println("${s.name} is from ${s.university}, ${s.age} years old, and likes ${s.hobby}")
}
為了保證生成代碼的一致性和有意義的方法袭祟,數(shù)據(jù)類對象必須滿足一下要求
- 構(gòu)造函數(shù)必須至少有一個參數(shù)
- 所有的構(gòu)造函數(shù)參數(shù)必須標(biāo)注val或者var验残;
- 數(shù)據(jù)類對象不能是 abstract, open, sealed or inner;
- 數(shù)據(jù)類對象不能繼承其他類,但是可以實現(xiàn)接口
- 在Java虛擬機里巾乳,如果生成的類需要有一個無參數(shù)的構(gòu)造函數(shù)您没,所有屬性的默認(rèn)值必須有一個具體的值,
componentN()
類對象里想鹰,有幾個屬性紊婉,會有幾個這個方法componentN(),比如上面的示例辑舷,調(diào)用方法 s.name 和 s.component1() 效果是一樣的
復(fù)制
我們經(jīng)常有這么一個使用場景:我么需要復(fù)制一個類對象喻犁,但是改變它的某些屬性,保持剩余其他的屬性值不變何缓。這就需要用到copy()函數(shù)實現(xiàn)肢础。對于上面的User類,可以如下實現(xiàn)復(fù)制:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
// 調(diào)用
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)