kotlin基礎語法
最近在學kotlin,這是本人看菜鳥教程後所做的筆記,很多會內容和菜鳥教程重複,僅供參考
基礎語法
包宣告
//類在包的位置 package com.demo.main import java.util.*
函式定義
函式定義使用關鍵字 fun,引數格式為:引數 : 型別
fun printSum(a: Int,b: Int){ println(a+b) }
無返回值的函式
public fun printSum(a: Int, b: Int) { print(a + b) }
可變長引數函式
fun vars(vararg a:String){ for(str in a){ println(str) } }
定義常量與變數
可變變數定義:var 關鍵字
var <識別符號> : <型別> = <初始化值>
不可變變數定義:val關鍵字,只能賦值一次的變數Java中final修飾的變數)
val <識別符號> : <型別> = <初始化值>
註釋
// 這是一個單行註釋
/* 這是一個多行的
塊註釋。 */
字串模板
$ 表示一個變數名或者變數值
$varName 表示變數值
${varName.fun()} 表示變數的方法返回值:
資料型別
型別 | 位寬度 |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
比較兩個數字
在 Kotlin 中,三個等號 === 表示比較物件地址,兩個 == 表示比較兩個值大小。
型別轉換
由於不同的表示方式,較小型別並不是較大型別的子型別,較小的型別不能隱式轉換為較大的型別。 這意味著在不進行顯式轉換的情況下我們不能把 Byte 型值賦給一個 Int 變數。
val b: Byte = 10 // OK, 字面值是靜態檢測的 val i: Int = b // 錯誤
我們可以代用其toInt()方法。
val b: Byte = 1 // OK, 字面值是靜態檢測的 val i: Int = b.toInt() // OK
每種資料型別都有下面的這些方法,可以轉化為其它的型別:
toByte(): Byte toShort(): Short toInt(): Int toLong(): Long toFloat(): Float toDouble(): Double toChar(): Char
有些情況下也是可以使用自動型別轉化的,前提是可以根據上下文環境推斷出正確的資料型別而且數學操作符會做相應的過載。例如下面是正確的:
val l = 1L + 3 // Long + Int => Long
條件控制
IF 表示式
一個 if 語句包含一個布林表示式和一條或多條語句。
var max = a if (a < b) max = b // 使用 else var max: Int if (a > b) { max = a } else { max = b } // 作為表示式 val max = if (a > b) a else b
When 表示式
when最簡單的形勢如下:
fun whenTest(a : Int){ when(a){ 1->{ println(1) } 2->{ println(2) } 3->{ println(3) } else->{ println("其他") } } }
when 也可以用來取代 if-else if鏈。 如果不提供引數,所有的分支條件都是簡單的布林表示式,而當一個分支的條件為真時則執行該分支:
when { x.isOdd() -> print("x is odd") x.isEven() -> print("x is even") else -> print("x is funny") }
迴圈控制
For 迴圈
迴圈體可以是一個程式碼塊:
for (item: Int in ints) { // …… }
while 與 do...while 迴圈
while是最基本的迴圈,它的結構為:
while( 布林表示式 ) { //迴圈內容 }
do…while 迴圈 對於 while 語句而言,如果不滿足條件,則不能進入迴圈。但有時候我們需要即使不滿足條件,也至少執行一次。
do…while 迴圈和 while 迴圈相似,不同的是,do…while 迴圈至少會執行一次。
do { //程式碼語句 }while(布林表示式);
返回和跳轉
Kotlin 有三種結構化跳轉表示式:
- return。預設從最直接包圍它的函式或者匿名函式 返回。
- break。終止最直接包圍它的迴圈。
- continue。繼續下一次最直接包圍它的迴圈。
類和物件
類定義
Kotlin 中使用關鍵字 class 宣告類,後面緊跟類名:
class Runoob {// 類名為 Runoob // 大括號內是類體構成 }
也可以定義一個空類:
class Empty
屬性定義
類的屬性可以用關鍵字 var 宣告為可變的,否則使用只讀關鍵字 val 宣告為不可變。
我們可以像使用普通函式那樣使用建構函式建立類例項:
val site = Runoob() // Kotlin 中沒有 new 關鍵字
要使用一個屬性,只要用名稱引用它即可
site.name// 使用 . 號來引用 site.url
主構造器
主構造器中不能包含任何程式碼,初始化程式碼可以放在初始化程式碼段中,初始化程式碼段使用 init 關鍵字作為字首。
class Person constructor(firstName: String) { init { println("FirstName is $firstName") } }
次建構函式
類也可以有二級建構函式,需要加字首 constructor:
class Person { constructor(parent: Person) { parent.children.add(this) } }
如果類有主建構函式,每個次建構函式都要,或直接或間接通過另一個次建構函式代理主建構函式。在同一個類中代理另一個建構函式使用 this 關鍵字:
class Person(val name: String) { constructor (name: String, age:Int) : this(name) { // 初始化... } }
抽象類
抽象是面向物件程式設計的特徵之一,類本身,或類中的部分成員,都可以宣告為abstract的。抽象成員在類中不存在具體的實現。
abstract class father{ abstract fun f() } class childs : father() { override fun f() { } }
巢狀類
class Outer {// 外部類 private val bar: Int = 1 class Nested {// 巢狀類 fun foo() = 2 } } fun main(args: Array<String>) { val demo = Outer.Nested().foo() // 呼叫格式:外部類.巢狀類.巢狀類方法/屬性 println(demo)// == 2 }
內部類
內部類使用 inner 關鍵字來表示。
內部類會帶有一個對外部類的物件的引用,所以內部類可以訪問外部類成員屬性和成員函式。
class Outer { private val bar: Int = 1 var v = "成員屬性" /**巢狀內部類**/ inner class Inner { fun foo() = bar// 訪問外部類成員 fun innerTest() { var o = this@Outer //獲取外部類的成員變數 println("內部類可以引用外部類的成員,例如:" + o.v) } } } fun main(args: Array<String>) { val demo = Outer().Inner().foo() println(demo) //1 val demo2 = Outer().Inner().innerTest() println(demo2)// 內部類可以引用外部類的成員,例如:成員屬性 }
匿名內部類
使用物件表示式來建立匿名內部類:
class Test { var v = "成員屬性" fun setInterFace(test: TestInterFace) { test.test() } } /** * 定義介面 */ interface TestInterFace { fun test() } fun main(args: Array<String>) { var test = Test() /** * 採用物件表示式來建立介面物件,即匿名內部類的例項。 */ test.setInterFace(object : TestInterFace { override fun test() { println("物件表示式建立匿名內部類的例項") } }) }
類的修飾符
類的修飾符包括 classModifier 和accessModifier :
- classModifier: 類屬性修飾符,標示類本身特性。
abstract// 抽象類 final// 類不可繼承,預設屬性 enum// 列舉類 open// 類可繼承,類預設是final的 annotation// 註解類
- accessModifier: 訪問許可權修飾符
private// 僅在同一個檔案中可見 protected// 同一個檔案中或子類可見 public// 所有呼叫的地方都可見 internal// 同一個模組中可見
繼承
Kotlin 中所有類都繼承該 Any 類,它是所有類的超類,對於沒有超型別宣告的類是預設超類:
class Example // 從 Any 隱式繼承
Any 預設提供了三個函式:
equals() hashCode() toString()
注意:Any 不是 java.lang.Object。
如果一個類要被繼承,可以使用 open 關鍵字進行修飾。
open class father(p: Int)// 定義基類 class Derived(p: Int) : father(p)
建構函式
如果子類有主建構函式, 則基類必須在主建構函式中立即初始化。
open class Person(var name : String, var age : Int){// 基類 } class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) { } // 測試 fun main(args: Array<String>) { val s =Student("Runoob", 18, "S12346", 89) println("學生名: ${s.name}") println("年齡: ${s.age}") println("學生號: ${s.no}") println("成績: ${s.score}") }
子類沒有主建構函式
如果子類沒有主建構函式,則必須在每一個二級建構函式中用 super 關鍵字初始化基類,或者在代理另一個建構函式。初始化基類時,可以呼叫基類的不同構造方法。
/**使用者基類**/ open class Person(name:String){ /**次級建構函式**/ constructor(name:String,age:Int):this(name){ //初始化 println("-------基類次級建構函式---------") } } /**子類繼承 Person 類**/ class Student:Person{ /**次級建構函式**/ constructor(name:String,age:Int,no:String,score:Int):super(name,age){ println("-------繼承類次級建構函式---------") println("學生名: ${name}") println("年齡: ${age}") println("學生號: ${no}") println("成績: ${score}") } }
介面
Kotlin 介面與 Java 8 類似,使用 interface 關鍵字定義介面,允許方法有預設實現:
interface MyInterface{ fun bar() fun foo(){ println("foo") } } class MyClass : MyInterface{ override fun bar() { println("bar") } } fun main(args:Array<String>){ var myClass=MyClass() myClass.bar() myClass.foo() }
介面中的屬性
介面中的屬性只能是抽象的,不允許初始化值,介面不會儲存屬性值,實現介面時,必須重寫屬性:
interface MyInterface{ var name:String } class MyClass : MyInterface{ override var name="Jack" } fun main(args:Array<String>){ var myClass=MyClass() println(myClass.name) }
函式重寫
實現多個介面時,可能會遇到同一方法繼承多個實現的問題。例如:
interface MyInterface2{ fun foo() fun bar(){ println("bar2") } } class MyClass : MyInterface,MyInterface2{ override fun bar() { super<MyInterface2>.bar() } override fun foo() { println("foo2") } } fun main(args:Array<String>){ var myClass=MyClass() myClass.foo() myClass.bar() }
當一個有實現方法,一個沒有實現方法時,預設是沒有實現方法的,super<MyInterface>.foo()切換實現方法,兩個都有時也可以通過這個方式切換實現方法
泛型
泛型,即 "引數化型別",將型別引數化,可以用在類,介面,方法上。
與 Java 一樣,Kotlin 也提供泛型,為型別安全提供保證,消除型別強轉的煩惱。
宣告一個泛型類:
class Class<T>(t: T) { var value = t }
型變
Kotlin 中沒有萬用字元型別,它有兩個其他的東西:宣告處型變(declaration-site variance)與型別投影(type projections)。
宣告處的型別變異使用協變註解修飾符:in、out,消費者 in, 生產者 out。
使用 out 使得一個型別引數協變,協變型別引數只能用作輸出,可以作為返回值型別但是無法作為入參的型別:
in 使得一個型別引數逆變,逆變型別引數只能用作輸入,可以作為入參的型別但是無法作為返回值的型別:
class demo<out A>(val a: A) { fun foo(): A { return a } } class demo2<in A>(a: A) { fun foo(a: A) { } }
泛型約束
我們可以使用泛型約束來設定一個給定引數允許使用的型別。
Kotlin 中使用 : 對泛型的的型別上限進行約束。
fun <T : Comparable<T>> sort(list: List<T>) { // …… }
列舉類
列舉類最基本的用法是實現一個型別安全的列舉。
列舉常量用逗號分隔,每個列舉常量都是一個物件。
enum class Color{ RED,BLACK,BLUE,GREEN,WHITE } fun main(args: Array<String>) { var color:Color=Color.BLUE println(Color.values()) println(Color.valueOf("RED")) println(color.name) println(color.ordinal) }
物件表示式和物件宣告
物件可以繼承於某個基類,或者實現其他介面:
open class A(x: Int) { public open val y: Int = x } interface B { } fun main(args:Array<String>){ val ab: A = object : A(1), B { override val y = 15 } println(ab.y) }
通過物件表示式可以越過類的定義直接得到一個物件:
val site = object { var name: String = "Jack" } println(site.name)
匿名物件可以用作只在本地和私有作用域中宣告的型別。如果你使用匿名物件作為公有函式的 返回型別或者用作公有屬性的型別,那麼該函式或屬性的實際型別 會是匿名物件宣告的超型別,如果你沒有宣告任何超型別,就會是 Any。在匿名物件 中新增的成員將無法訪問。
class A { // 私有函式,所以其返回型別是匿名物件型別 private fun foo() = object { val x: String = "x" } // 公有函式,所以其返回型別是 Any fun Foo2() = object { val x: String = "x" } fun bar() { val x1 = foo().x// 沒問題 val x2 = publicFoo().x// 錯誤:未能解析的引用“x” } }
物件宣告
Kotlin 使用 object 關鍵字來宣告一個物件。
object Test{ fun foo(){ println("Jack") } } fun main(args:Array<String>){ Test.foo() }
當然你也可以定義一個變數來獲取獲取這個物件,當時當你定義兩個不同的變數來獲取這個物件時,你會發現你並不能得到兩個不同的變數。也就是說通過這種方式,我們獲得一個單例。
object My { var name: String = "JACK" } fun main(args:Array<String>){ var s1 =My var s2 = My s1.name = "HelloJack" println(s1.name) println(s2.name) }
伴生物件
類內部的物件宣告可以用 companion 關鍵字標記,這樣它就與外部類關聯在一起,我們就可以直接通過外部類訪問到物件的內部元素。
class MyClass { companion object Factory { fun create(): MyClass = MyClass() } fun test(){ println("Jack") } } fun main(args:Array<String>){ val instance = MyClass.create() instance.test() }
我們可以省略掉該物件的物件名,然後使用 Companion 替代需要宣告的物件名:
class MyClass { companion object { } } val x = MyClass.Companion
注意:一個類裡面只能宣告一個內部關聯物件,即關鍵字 companion 只能使用一次。
類委託
類的委託即一個類中定義的方法實際是呼叫另一個類的物件的方法來實現的。
// 建立介面 interface Base { fun print() } // 實現此介面的被委託的類 class BaseImpl(val x: Int) : Base { override fun print() { print(x) } } // 通過關鍵字 by 建立委託類 class Derived(b: Base) : Base by b fun main(args:Array<String>){ val b = BaseImpl(10) Derived(b).print() // 輸出 10 }
lazy() 是一個函式, 接受一個 Lambda 表示式作為引數, 返回一個 Lazy <T> 例項的函式,返回的例項可以作為實現延遲屬性的委託: 第一次呼叫 get() 會執行已傳遞給 lazy() 的 lamda 表示式並記錄結果, 後續呼叫 get() 只是返回記錄的結果。
val MyLazyValue: String by lazy { println("computed!")// 第一次呼叫輸出,第二次呼叫不執行 "Hello" } fun main(args:Array<String>){ println(MyLazyValue) println(MyLazyValue) }
一個常見的用例是在一個對映(map)裡儲存屬性的值。 這經常出現在像解析 JSON 或者做其他"動態"事情的應用中。 在這種情況下,你可以使用對映例項自身作為委託來實現委託屬性。
class Data(val map: Map<String, Any?>) { val name: String by map val url: Stringby map } fun main(args:Array<String>){ val site = Data(mapOf( "name" to "Jack1", "url"to "Jack2" )) // 讀取對映值 println(site.name) println(site.url) }