Kotlin 語法基礎(chǔ)大全亏镰,從例子著手的 從0到1的學(xué)習(xí) -- 基礎(chǔ)介紹
Kotlin 語法基礎(chǔ)大全,從例子著手的 從0到1的學(xué)習(xí) -- 流程控制
Kotlin 語法基礎(chǔ)大全拯爽,從例子著手的 從0到1的學(xué)習(xí) -- 特殊的類
Kotlin 語法基礎(chǔ)大全索抓,從例子著手的 從0到1的學(xué)習(xí) -- 函數(shù)
Kotlin 語法基礎(chǔ)大全,從例子著手的 從0到1的學(xué)習(xí) -- 集合
Kotlin 語法基礎(chǔ)大全毯炮,從例子著手的 從0到1的學(xué)習(xí) -- 作用域
Kotlin 語法基礎(chǔ)大全逼肯,從例子著手的 從0到1的學(xué)習(xí) -- 代理
Kotlin 語法基礎(chǔ)大全,從例子著手的 從0到1的學(xué)習(xí) -- 產(chǎn)品級特性
翻譯來源
hello world
package org.kotlinlang.play // 1
fun main() { // 2
println("Hello, World!") // 3
}
- 1 kotlin 一般定義在包里桃煎。但是如果你不定義那么就將內(nèi)容放到默認的包中篮幢。
- 2
main
方法是程序的入口。 在 Kotlin 1.3main
方法可以不定義任何參數(shù)也不需要定義返回值为迈。 - 3
println
方法將會把一行內(nèi)容輸入到標(biāo)準輸出三椿。這個方法已經(jīng)默認引用了,不需要顯式的引用葫辐。分號也不再是必須的搜锰。
在早于1.3版本main
方法必須要有參數(shù)
fun main(args: Array<String>) {
println("Hello, World!")
}
方法
普通方法
fun printMessage(message: String): Unit { // 1
println(message)
}
fun printMessageWithPrefix(message: String, prefix: String = "Info") { // 2
println("[$prefix] $message")
}
fun sum(x: Int, y: Int): Int { // 3
return x + y
}
fun multiply(x: Int, y: Int) = x * y // 4
fun main() {
printMessage("Hello") // 5
printMessageWithPrefix("Hello", "Log") // 6
printMessageWithPrefix("Hello") // 7
printMessageWithPrefix(prefix = "Log", message = "Hello") // 8
println(sum(1, 2)) // 9
}
- 1 一個簡單的方法,帶有一個string參數(shù)和返回unit 就是返回void 沒有返回值
- 2 一個帶有兩個參數(shù)的方法耿战,其中 info是一個帶有默認值的方法纽乱,無返回值就是默認Unit
通過tools show kotlin bytecode 再decompile 可以看到kotlin的java代碼長什么樣子
fun printMessageWithPrefix(message: String, prefix: String = "Info") { // 2 println("[$prefix] $message") } fun main() { printMessageWithPrefix("1") printMessageWithPrefix("1", "2") }
kotlin代碼轉(zhuǎn)化之后成為的樣子,·從這可以看到kotlin在這里的處理是創(chuàng)建一個不同名字的方法昆箕,直接改了調(diào)用的地方,不是如預(yù)想中的重載·
public static final void printMessageWithPrefix(@NotNull String message, @NotNull String prefix) { Intrinsics.checkParameterIsNotNull(message, "message"); Intrinsics.checkParameterIsNotNull(prefix, "prefix"); String var2 = '[' + prefix + "] " + message; boolean var3 = false; System.out.println(var2); } // $FF: synthetic method public static void printMessageWithPrefix$default(String var0, String var1, int var2, Object var3) { if ((var2 & 2) != 0) { var1 = "Info"; } printMessageWithPrefix(var0, var1); } public static final void main() { printMessageWithPrefix$default("1", (String)null, 2, (Object)null); printMessageWithPrefix("1", "2"); } // $FF: synthetic method public static void main(String[] var0) { main(); }
- 3 一個返回int的值的方法
- 4 只有一個表達式的方法租冠,并且推斷返回值為int
- 5 調(diào)用了第一個方法傳入了一個
hello
的參數(shù) - 6 調(diào)用了第二個方法傳入了兩個參數(shù)
- 7 調(diào)用了第二個方法傳入了一個參數(shù)鹏倘,第二個參數(shù)info帶有默認值info
- 8 調(diào)用了第二個方法傳入了帶命名的參數(shù),這使得參數(shù)不再是按照參數(shù)順序來傳入的
- 9 打印方法的返回值
infix functions 中綴方法
如果成員方法和擴展方法只有一個參數(shù)那么可以轉(zhuǎn)化為中綴方法
fun main() {
infix fun Int.times(str: String) = str.repeat(this) // 1
println(2 times "Bye ") // 2
val pair = "Ferrari" to "Katrina" // 3
println(pair)
infix fun String.onto(other: String) = Pair(this, other) // 4
val myPair = "McLaren" onto "Lucas"
println(myPair)
val sophia = Person("Sophia")
val claudia = Person("Claudia")
sophia likes claudia // 5
}
class Person(val name: String) {
val likedPeople = mutableListOf<Person>()
infix fun likes(other: Person) { likedPeople.add(other) } // 6
}
- 1 定義一個中綴擴展方法在 Int 類里
- 2 嗲用這個方法
- 3 創(chuàng)建一個pair 通過標(biāo)準庫里的中綴方法
to
- 4 創(chuàng)建一個你自己的 類似于
to
的方法onto
- 5 中綴符號 也可以定義成 成員方法顽爹,用于類對象使用
- 6 定義了中綴方法的類將會成為這個方法里面的第一個參數(shù)纤泵,即有個默認的this
local functions 內(nèi)部方法
定義在方法中的方法
操作符方法
明確的方法可以升級為 操作符方法,允許調(diào)用者使用相應(yīng)的符號進行運算
operator fun Int.times(str: String) = str.repeat(this) // 1
println(2 * "Bye ") // 2
operator fun String.get(range: IntRange) = substring(range) // 3
val str = "Always forgive your enemies; nothing annoys them so much."
println(str[0..14]) //14
- 1 操作符和對應(yīng)的重載方法是一一對應(yīng)的 times 方法就是對應(yīng) *的,相當(dāng)于重載操作符了
- 2
times
對應(yīng)了*
你調(diào)用2 * "Bye "
相當(dāng)與 調(diào)用了2.times("Bye ")
- 3 get(range: IntRange) ==> [0 .. 14]
- 4 可以參考這篇文章
帶有 vararg 參數(shù)的方法
vararg 就相當(dāng)于 java 中 String... args
fun printAll(vararg messages: String) { // 1
for (m in messages) println(m)
}
printAll("Hello", "Hallo", "Salut", "Hola", "你好") // 2
fun printAllWithPrefix(vararg messages: String, prefix: String) { // 3
for (m in messages) println(prefix + m)
}
printAllWithPrefix(
"Hello", "Hallo", "Salut", "Hola", "你好",
prefix = "Greeting: " // 4
)
fun log(vararg entries: String) {
printAll(*entries) // 5
}
- 1 如何獲取 vararg中的每個參數(shù)
- 2 你也可以傳入任意數(shù)量的參數(shù)包括0個
- 3 由于有命名參數(shù)的存在你可以在可變長度的參數(shù)后面再添加一個同類型的參數(shù)镜粤,二這一點在java中是做不到的
- 4 你應(yīng)該像這樣一樣使用
- 5 在運行時中捏题,vararg 是以數(shù)組的形式傳遞到參數(shù)中,這和java是一樣的肉渴。你可以用
*
將可變長度的參數(shù)以可變長度的參數(shù)來傳遞而不是以數(shù)組的方式來傳遞公荧。這里不加 * 將會報錯。
變量
kotlin 具有強大的變量的推斷能力同规。你可以明確的定義變量的類型循狰,也可以讓編譯器自己來推斷變量類型是什么窟社。推薦使用val 來定義變量,因為這是不變的變量绪钥,當(dāng)然kotlin 也不會強制你將變量定義為不變灿里,你可以使用var。
var a: String = "initial" // 1
println(a)
val b: Int = 1 // 2
val c = 3 // 3
- 1 定義了一個可變變量程腹,并且初始化
- 2 定義了一個不可變變量匣吊,并且初始化。相當(dāng)于java中添加了 final
- 3 定義了一個不可變變量寸潦,并且初始化色鸳,并且沒有明確定義變量名稱。類型將由編譯器推斷甸祭,這里編譯器推斷的是Int
var e: Int // 1
println(e) // 2
- 1 定義了一個變量并且沒有對他進行初始化
- 2 這里將會報錯缕碎,變量必須進行初始化
你可以在任何地方進行初始化,只要在read 這個變量之前就可以池户。這里和java 不同
val d: Int // 1
if (someCondition()) {
d = 1 // 2
} else {
d = 2 // 2
}
println(d) // 3
- 1 定義一個不可變的變量
- 2 根據(jù)一些條件進行判斷 之后再初始化
- 3 read 這個變量咏雌,這里不報錯,因為已經(jīng)初始化了
空安全檢查
kotlin 想要創(chuàng)造一個沒有NullPointerException
的世界校焦,所以不允許給一個變量賦予Null值赊抖。如果你確實需要賦予一個變量null,請在他的類型名稱之后加上?
var neverNull: String = "This can't be null" // 1
neverNull = null // 2
var nullable: String? = "You can keep a null here" // 3
nullable = null // 4
var inferredNonNull = "The compiler assumes non-null" // 5
inferredNonNull = null // 6
fun strLength(notNull: String): Int { // 7
return notNull.length
}
strLength(neverNull) // 8
strLength(nullable) // 9
- 1 定義了一個不可為空的String寨典,并且賦值
- 2 將這個字符串設(shè)置為null(這里將會報錯)
- 3 定義一個可為空的String氛雪,并且賦值
- 4 將這個字符串設(shè)置為null,這里沒有報錯
- 5 定義一個由編譯器進行推斷的變量耸成,這里編譯器推斷為String报亩。編譯器將會把變量優(yōu)先推斷為非空類型
- 6 報錯,因為編譯器進行推斷的時候都將
- 7 定義了一個方法井氢,傳入的是非空的參數(shù)String
- 8 傳入的是非空的String值弦追,這里將不會報錯
- 9 傳入的可為空的String? 值,這里將會報錯
有時候你必須處理null值花竞,可能是因為Java代碼傳入了null 也可能是null代表了一種狀態(tài)劲件,這時候kotlin提供了一種處理null的方法
fun describeString(maybeString: String?): String { // 1
if (maybeString != null && maybeString.length > 0) { // 2
return "String of length ${maybeString.length}"
} else {
return "Empty or null string" // 3
}
}
- 1 定義了一個方法可入可為空的String 類型,返回一個不可為空的String類型
- 2 判斷maybeString 是否為空或者為空字符串约急,如果是就返回maybeString的長度
- 3 不然的話就告知調(diào)用者零远,傳入的參數(shù)為空或者為空字符串
類
類定義由類名,類頭(包含類型聲明厌蔽,私有構(gòu)造方法等)牵辣、和類體組成,并且由大括號組成奴饮。類頭和類體都是可以缺省的服猪。如果類體缺省供填,那么大括號也可以缺省。
class Customer // 1
class Contact(val id: Int, var email: String) // 2
fun main() {
val customer = Customer() // 3
val contact = Contact(1, "mary@gmail.com") // 4
println(contact.id) // 5
contact.email = "jane@gmail.com" // 6
}
- 1 定義一個類Customer 罢猪,這個類沒有定義任何屬性和構(gòu)造方法近她。 kotlin將會自動賦予一個無參構(gòu)造方法
- 2 定義一個Contact 類,類里面有一個不可變變量的id膳帕,一個可變的變量email粘捎,并且有一個帶有兩個參數(shù)的構(gòu)造方法
- 3 創(chuàng)建一個不可變的變量 實例customer類型推斷為Customer。使用Customer的默認構(gòu)造方法危彩。在kotlin中是沒有new關(guān)鍵字的
- 4 創(chuàng)建一個Contact的實例攒磨,使用了帶有兩個參數(shù)的構(gòu)造方法
- 5 讀取contact的屬性id
- 6 更新contact的屬性email
泛型
泛型是現(xiàn)代語言都具有的一種模板方法。泛型類和泛型方法使得代碼具有高度的可復(fù)用性汤徽。就像List<T>
里賣弄的內(nèi)部邏輯與T就沒有什么邏輯上的關(guān)系
泛型類
第一種使用泛型的方法就是泛型類
class MutableStack<E>(vararg items: E) { // 1
private val elements = items.toMutableList()
fun push(element: E) = elements.add(element) // 2
fun peek(): E = elements.last() // 3
fun pop(): E = elements.removeAt(elements.size - 1)
fun isEmpty() = elements.isEmpty()
fun size() = elements.size
override fun toString() = "MutableStack(${elements.joinToString()})"
}
- 1 定義一個泛型類娩缰,E可以被稱為泛型參數(shù)。當(dāng)你定義
MutableStack<Int>
的時候Int 就被稱呼為泛型參數(shù) - 2 在泛型類里面的方法中你可以用E來代替泛型參數(shù)
- 3 你也可以用E來代替返回值
泛型方法
你當(dāng)然也可以定義泛型方法谒府,如果他們的邏輯與泛型參數(shù)無關(guān)的話拼坎。比如你可以定義一個這樣的方法
fun <E> mutableStackOf(vararg elements: E) = MutableStack(*elements)
fun main() {
val stack = mutableStackOf(0.62, 3.14, 2.7)
println(stack)
}
注意:編譯將會推斷出mutableStackOf是一個Double的MutableStack所以你不需要填寫mutableStackOf<Double>(...)
之類的
繼承
kotlin 支持傳統(tǒng)的面向?qū)ο蟮睦^承機制
open class Dog { // 1
open fun sayHello() { // 2
println("wow wow!")
}
}
class Yorkshire : Dog() { // 3
override fun sayHello() { // 4
println("wif wif!")
}
}
fun main() {
val dog: Dog = Yorkshire()
dog.sayHello()
}
- 1 kotlin 的類默認是final的,如果你要允許類可以被繼承完疫,你需要添加 open 關(guān)鍵字到class 之前泰鸡。
- 2 kotlin的方法默認也是final的,如果你要允許方法可以被重寫,你需要添加open關(guān)鍵字到fun 之前。
- 3 一個子類應(yīng)該定義在
: SuperclassName()
之前夜牡。空的()
意味著super了父類的默認構(gòu)造方法余舶。 - 4 重寫的方法胡總和屬性,應(yīng)該添加override 關(guān)鍵字
帶有參數(shù)的構(gòu)造方法的繼承
open class Tiger(val origin: String) {
fun sayHello() {
println("A tiger from $origin says: grrhhh!")
}
}
class SiberianTiger : Tiger("Siberia") // 1
fun main() {
val tiger: Tiger = SiberianTiger()
tiger.sayHello()
}
- 1 如果你想要用父類的構(gòu)造方法定義一個子類锹淌,那么你需要在定義的時候傳入傳給父類的字段欧芽。
傳遞參數(shù)給父類
open class Lion(val name: String, val origin: String) {
fun sayHello() {
println("$name, the lion from $origin says: graoh!")
}
}
class Asiatic(name: String) : Lion(name = name, origin = "India") // 1
fun main() {
val lion: Lion = Asiatic("Rufo") // 2
lion.sayHello()
}
- 1
Asiatic
類里的name 既不是val 也不是var。這是一個構(gòu)造方法的參數(shù)葛圃,只用來將值傳遞給父類的的構(gòu)造方法。 - 2 用構(gòu)造參數(shù)Rufo創(chuàng)建一個Asiatic的實例憎妙,這個調(diào)用將會嗲用父類
Lion
的雙參構(gòu)造方法