Java轉(zhuǎn)Kotlin,對語法、聲明和一些符號的使用不是很清楚,特此記錄
變量定義方式
val 只讀變量,只能賦值一次
var 讀寫變量,可多次賦值
val a: Int = 1 // 立即賦值
val b = 2 // 自動(dòng)推斷出 `Int` 類型
val c: Int // 如果沒有初始值類型不能省略
c = 3 // 明確賦值
理解自動(dòng)類型推斷
函數(shù)的定義
//帶有兩個(gè) Int 參數(shù)、返回 Int 的函數(shù):
fun sum(a: Int, b: Int): Int {
return a + b
}
//將表達(dá)式作為函數(shù)體、返回值類型自動(dòng)推斷的函數(shù):
fun sum(a: Int, b: Int) = a + b
//將表達(dá)式作為函數(shù)體、返回值類型自動(dòng)推斷的函數(shù):
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")
}
//Unit 返回類型可以省略:
fun printSum(a: Int, b: Int) {
println("sum of $a and $b is ${a + b}")
}
模板字符串
在字符串中引用變量,或者在字符串中使用任意表達(dá)式
var a = 1
val s1 = "a is $a1"
a = 2
val s2 = "${s1.replace("is", "was")}, but now is $a"
s2的輸出結(jié):a was 1, but now is 2
表達(dá)式
與Java類似,if(){},if(){}else{}
fun maxOf(a: Int, b: Int): Int {
if (a > b) {
return a
} else {
return b
}
}
fun maxOf(a: Int, b: Int) = if (a > b) a else b
兩種寫法都一樣
空值檢測和和使用
當(dāng)某個(gè)變量的值可以為 null 的時(shí)候,必須在聲明處的類型后添加 ? 來標(biāo)識該引用可為空
fun parseInt(str: String): Int? {
//表示該方法可以返回一個(gè)整型或者null
}
使用類型檢測及自動(dòng)類型轉(zhuǎn)換
is 運(yùn)算符檢測一個(gè)表達(dá)式是否某類型的一個(gè)實(shí)例。 如果一個(gè)不可變的局部變量或?qū)傩砸呀?jīng)判斷出為某類型,那么檢測后的分支中可以直接當(dāng)作該類型使用,無需顯式轉(zhuǎn)換:
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// `obj` 在該條件分支內(nèi)自動(dòng)轉(zhuǎn)換成 `String`
return obj.length
}
// 在離開類型檢測分支后,`obj` 仍然是 `Any` 類型
return null
}
fun getStringLength(obj: Any): Int? {
// `obj` 在 `&&` 右邊自動(dòng)轉(zhuǎn)換成 `String` 類型
if (obj is String && obj.length > 0) {
return obj.length
}
return null
}
emmmm.....這里的is和Java的instanceof是一個(gè)意思,其中is作用范圍內(nèi)自動(dòng)轉(zhuǎn)換數(shù)據(jù)類型比較Java方便了不少
if條件表達(dá)式
// 傳統(tǒng)用法
var max = a
if (a < b) max = b
// With else
var max: Int
if (a > b) {
max = a
} else {
max = b
}
// 作為表達(dá)式
val max = if (a > b) a else b
//if的分支可以是代碼塊,最后的表達(dá)式作為該塊的值
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
與Java的if不同在于,Kotlin的if可以作為語句,也可一作為表達(dá)式,當(dāng)它作為表達(dá)式時(shí),每一個(gè)分支中最后一個(gè)表達(dá)式的值為分支的值
When表達(dá)式
Kotlin使用when取代了switch
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意這個(gè)塊
print("x is neither 1 nor 2")
}
}
//多分支合并,和switch真的很像
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
//任意表達(dá)是作為分支
when (x) {
parseInt(s) -> print("s encodes x")
else -> print("s does not encode x")
}
//任意區(qū)間判斷作為分支
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
//Since Kotlin 1.3, it is possible to capture when subject in a variable using following syntax:
//強(qiáng)大到令人發(fā)指??
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
和if一樣,when也可以作為表示式使用,但是需要注意:如果 when 作為一個(gè)表達(dá)式使用,則必須有 else 分支, 除非編譯器能夠檢測出所有的可能情況都已經(jīng)覆蓋了
伴生對象
類內(nèi)部的對象聲明可以用 companion 關(guān)鍵字標(biāo)記
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
該伴生對象的成員可通過只使用類名作為限定符來調(diào)用:
val instance = MyClass.create()
其自身所用的類的名稱(不是另一個(gè)名稱的限定符)可用作對該類的伴生對象 (無論是否命名)的引用
class MyClass1 {
companion object Named { }
}
val x = MyClass1
class MyClass2 {
companion object { }
}
val y = MyClass2
emmm...看起來很像Java的static,但是它不是static,請注意,即使伴生對象的成員看起來像其他語言的靜態(tài)成員,在運(yùn)行時(shí)他們?nèi)匀皇钦鎸?shí)對象的實(shí)例成員,而且,例如還可以實(shí)現(xiàn)接口
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
val f: Factory<MyClass> = MyClass
對象表達(dá)式和對象聲明之間有一個(gè)重要的語義差別:
對象表達(dá)式是在使用他們的地方**立即**執(zhí)行(及初始化)的
對象聲明是在第一次被訪問到時(shí)**延遲**初始化的
伴生對象的初始化是在相應(yīng)的類被加載(解析)時(shí),與 Java 靜態(tài)初始化器的語義相匹配
創(chuàng)建基本類和實(shí)例
不需要關(guān)鍵字new,直接類名稱就能創(chuàng)建一個(gè)對象
val rectangle = Rectangle(5.0, 2.0) // 不需要“new”關(guān)鍵字
val triangle = Triangle(3.0, 4.0, 5.0)
主構(gòu)造函數(shù)和次構(gòu)造函數(shù)以及初始化
在 Kotlin 中的一個(gè)類可以有一個(gè)主構(gòu)造函數(shù)以及一個(gè)或多個(gè)次構(gòu)造函數(shù)。主構(gòu)造函數(shù)是類頭的一部分:它跟在類名(與可選的類型參數(shù))后
class Person constructor(firstName: String) { ... }
//如果主構(gòu)造函數(shù)沒有任何注解或者可見性修飾符,可以省略這個(gè) constructor 關(guān)鍵字
class Person(firstName: String) { ... }
//事實(shí)上,聲明屬性以及從主構(gòu)造函數(shù)初始化屬性,Kotlin 有簡潔的語法:
class Person(val firstName: String, val lastName: String, var age: Int) { …… }
主構(gòu)造函數(shù)不能包含任何的代碼
初始化代碼應(yīng)該放在init{}中
在實(shí)例初始化期間,初始化塊按照它們出現(xiàn)在類體中的順序執(zhí)行,與屬性初始化器交織在一起
class InitOrderDemo(name: String) {
val firstProperty = "First property: $name".also(::println)
init {
println("First initializer block that prints ${name}")
}
val secondProperty = "Second property: ${name.length}".also(::println)
init {
println("Second initializer block that prints ${name.length}")
}
}
fun main() {
InitOrderDemo("hello")
}
First property: hello
First initializer block that prints hello
Second property: 5
Second initializer block that prints 5
如果一個(gè)非抽象類沒有聲明任何(主或次)構(gòu)造函數(shù),它會(huì)有一個(gè)生成的不帶參數(shù)的主構(gòu)造函數(shù)。構(gòu)造函數(shù)的可見性是 public
這一點(diǎn)和Java挺像的
類也可以聲明前綴有 constructor的次構(gòu)造函數(shù)
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
如果類有一個(gè)主構(gòu)造函數(shù),每個(gè)次構(gòu)造函數(shù)需要委托給主構(gòu)造函數(shù), 可以直接委托或者通過別的次構(gòu)造函數(shù)間接委托。委托到同一個(gè)類的另一個(gè)構(gòu)造函數(shù)用 this 關(guān)鍵字即可
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
屬性、訪問器、幕后字段、幕后屬性
這里我看api文檔是真的沒有看懂。。。。。。感謝依然范特希西大佬
Kotlin 什么是幕后字段?
接口
使用關(guān)鍵字 interface 來定義接口
Kotlin 的接口與 Java 8 類似,既包含抽象方法的聲明,也包含實(shí)現(xiàn)。
需要注意實(shí)現(xiàn)多個(gè)接口沖突的覆蓋
//接口的定義
interface MyInterface {
fun bar()
fun foo() {
// 可選的方法體
}
}
/**在接口中聲明的屬性要么是抽象的,要么提*供訪問器的實(shí)現(xiàn)。
在接口中聲明的屬性不能有幕后字段(backingfield),
因此接口中聲明的訪問器不能引用它們*/
interface MyInterface {
val prop: Int // 抽象的
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(prop)
}
}
//接口沖突的覆蓋和重寫
class Child : MyInterface {
override val prop: Int = 29
}
interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
override fun bar() {
super<B>.bar()
}
}
接口里面不僅是聲明方法,還可以定義方法體。。。但是和抽象類還是有很大的區(qū)別,與抽象類不同的是,接口無法保存狀態(tài)。它可以有屬性但必須聲明為抽象或提供訪問器實(shí)現(xiàn)。
訪問修飾符
- 如果你不指定任何可見性修飾符,默認(rèn)為
public,這意味著你的聲明將隨處可見; - 如果你聲明為
private,它只會(huì)在聲明它的文件內(nèi)可見; - 如果你聲明為
internal,它會(huì)在相同模塊內(nèi)隨處可見; -
protected不適用于頂層聲明。
對模塊的理解:可見性修飾符 internal 意味著該成員只在相同模塊內(nèi)可見。更具體地說, 一個(gè)模塊是編譯在一起的一套 Kotlin 文件:
- 一個(gè) IntelliJ IDEA 模塊;
- 一個(gè) Maven 項(xiàng)目;
- 一個(gè) Gradle 源集(例外是 test 源集可以訪問 main 的 internal 聲明);
- 一次 <kotlinc> Ant 任務(wù)執(zhí)行所編譯的一套文件。
擴(kuò)展函數(shù)和擴(kuò)展屬性
聲明一個(gè)擴(kuò)展函數(shù),我們需要用一個(gè) 接收者類型 也就是被擴(kuò)展的類型來作為他的前綴。 下面代碼為 MutableList<Int> 添加一個(gè)swap 函數(shù):
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”對應(yīng)該列表
this[index1] = this[index2]
this[index2] = tmp
}
與函數(shù)類似,Kotlin 支持?jǐn)U展屬性:
val <T> List<T>.lastIndex: Int
get() = size - 1
注意:由于擴(kuò)展沒有實(shí)際的將成員插入類中,因此對擴(kuò)展屬性來說幕后字段是無效的。這就是為什么擴(kuò)展屬性不能有初始化器。他們的行為只能由顯式提供的 getters/setters 定義。