Kotlin語法簡記
Kotlin語法簡記
一、基礎語法
包宣告
Kotlin語言的原始檔,不需要對應匹配檔案目錄和包。
預設包名
default
,都會預設匯入kotlin
下的相關必須包//包名中如果含有,kotlin 的關鍵字,則需要用 反引號 ` 來包裝一下 package `in`.zhiwei.aqi
函式定義
fun
關鍵字,格式為args:type
,返回型別// fun sum(a:Int,b:Int):Int{ return a+b }
若使用
lambad
類的表示式,返回型別會自動推斷。但:public
的函式,必須註明返回型別fun sum(a:Int,b:Int) = a+b //此時會自動推斷返回型別 public fun sum(a:Int,b:Int):Int = a+b //public函式,必須註明返回型別
Unit
無返回型別,類似於Java中的Void
fun printSum(a:Int,b:Int):Unit{ print(a+b) } //Unit型別,可以省略,public函式,也可以。
vararg
可變引數fun vars(vararg v:Int){ for(vt in v){ print(vt) } } //呼叫vars函式 fun main(args:Array<String>)
lambda
匿名函式fun main(args:Array<String>){ val sumLambda:(Int,Int)-> Int = {x,y -> x+y} println(sumLambda(1,2)) }
常量、變數
定義變數,常量,使用
var
,val
關鍵字,格式var/val name : Type = value
,其中Type可以省略,自動推算fun main(args:Array<String>){ var abc:Int = 3 val PI = 3.1415926
註釋
kotlin註釋,單行
//
,多行/* text */
且可以巢狀。字串模板
$
符號$
表示變數名、或變數值$varName
表示變數值${varName.fun()}
表示返回值
var a = 1 //變數值 val s1 = "a is $a" a = 2 //返回值 val s2 = "${s1.replace("is","was")},but now is $a"
NULL檢查機制
Kotlin的NULL檢查,兩種:
!!
符號,丟擲異常?
符號,不處理,返回值為null
。或者?:
三目運算
//當引用可能為null時候,型別宣告必須標記可為null //下面示例,str內容若非整數,則會null,所以返回型別必須宣告可為null fun parseInt(str:String):Int?{ //.... }
型別轉換
使用
is
、!is
fun getStringLenght(obj:Any):Int?{ if(obj is String){ //型別判斷後,會自動轉換 return obj.length } // !is表示,不是某個型別 return null }
區間
區間表示式,
..
的rangeTo,配合in
或!in
。注意範圍,數學中的左右閉區間[1,4]
for (i in 1..4) print(i) // output "1234" for (i in 4..1) print(i) // output nothing //指定步長 for (i in 1..4 step 2) print(i)// output "13" for (i in 4 downTo 1 step 2) print(i) // output "42" //使用until函式,排除結束的那個元素 for(i in 1 until 10){//[1,10) print(i) }
二、基礎資料型別
型別 | 位寬度 |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
- 字面常量
- 十進位制:123
- 長整形:123L
- 16進位制:0x0F
- 2進位制:0b00001011
- 注:不支援八進位制
- Doubles:123.5,123.5e10
- Floats:123.5f 或123.5F
kotlin
//可以使用下劃線,分隔數字,便宜閱讀
val oneMillion = 1_000_000
val hexBytes = 0xFF_EC_DE_5E
數字比較
在上面可以看出,Kotlin都是Double、Int、Float等封裝後的型別,沒有基礎的資料型別,避免空指標。
===
對比地址==
對比值
for main(args:Array<String>){ val a = 100 print(a===a)//true,自身對比,值與地址皆同 //包裝 val boxA:Int? = a val boxB:Int? = a //對比 print(boxA == boxB)//true 值相等 print(boxA === boxB)//false 物件不同,地址不同 }
型別轉換
不同於Java的小到大可以自動轉換,Kotlin不可以。
val b:Byte = 1 val i:Int = b //這就錯了,b是Byte,不能轉化為Int,他們都是包裝後的 val l = 1L + 3 //Int和Long這類還可以自動轉 //其他可以使用toByte,toInt等
位操作符
適用於Int和Long
shl(bits) - 左位移(Java中的<<) shr(bits) - 右位移(Java中的>>) ushr(bits) - 無符號右位移(Java中的>>>) and(bits) - & or(bits) - | xor(bits) - ^ inv() - reverse
字元
Kotlin中Char不可與數字直接操作,必須
'char'
單引號包裹c == '1'//不能寫作 c == 1 //特殊字元可以轉義,或者Unicode表示
Boolean
||、&&、!
Array
//[1,2,3] val arrA = arrayOf(1,2,3) val arrB = Array(3,{i -> (i*2)})
Kotlin中,陣列是不變的型別
字串String
- 可以
"""
三對雙引號,來多行 - for遍歷某個字元
- trimMargin()來刪除空白
val text = """ |第一行 |第二行 |第三行 """ //預設使用`|`作為邊界字首,也可以自定義 trimMargin(">")
- 可以
三、條件控制
if表示式
var max = a if(a<b){ max = b }else{ max = a } //或者,類似Java中的三目運算子 var max = if(a<b) a else b
使用區間
//x..y var a = 7 if(x in 1..9) print("a in this range")
when
類似其他程式語言中的
Swith...case
when(x){ 1-> print("x == 1") 2-> print("x == 2") 3,4-> print("x == 3 or 4") in 5..9-> print("x is in this range")//還有!in,is ,!is in validNumbers-> print("x is valid") else-> print("x not 1 or 2")//類似defalut }
四、迴圈控制
for迴圈
for(item in collection) print(item) for(i in array.indices) print(array[i]) for((index,value) in array.withIndex()) { print("item $index is $value") }
while,do…while
return、break、continue
//Break和Continue 可以配合label標籤,確定其作用範圍,格式[email protected] [email protected] for(i in 1..100){ for(j in 1..100){ if(...) break@loop } } //return 返回,也可以用label標籤確定範圍 fun foo(){ ints.forEach{ if(it == 0)return//這麼return,就return了真個foo函式 print(it) } } //配合lable fun foo(){ ints.forEach [email protected]{ if(it == 0)return@lit//如此,只return出forEach, print(it) } } //可以不用顯示宣告label, fun foo(){ ints.forEach{ if(it == 0)return@forEach//隱式label的使用 print(it) } } //也可以,將lambad中宣告匿名函式,return就會在它的範圍內return fun foo(){ ints.forEach(fun(value:Int)){ if(it==0)return print(it) } } //return帶有返回值的時候,帶有label的return,優先 return@abc 1//從標籤abc那裡,返回出去數值 1
五、類和物件
//定義類
class Demo{
var name:String = "Joe"
var url:String = "www.zhiwei.in"
val PI = 3.1415926
//成員函式
fun foo(){
print("Foo")
}
}
//空類
class Empty
//例項化
val demo = Demo()//kotlin 中沒有new
demo.name
//建構函式,一個主的,可有多個次的,主建構函式,就在class標題處
class Person constructor(firstName:String){
//...
}
//若主建構函式,方法體為空,則constructor可以省略
class Person(firstName:String){}
//geter setter 可選
var lastName:String = "Green"
get()=field.toUpperCase()
set
var no:Int = 100
get() = field
set(value){
...
}
var height:Float = 175.0f
private set
//field關鍵字,用於後端變數,kotlin中不能有欄位
//非空屬性必須定義時候就初始化,kotlin使用lateinit來延遲初始化
public class MyTest{
lateinit var subject:TestSubject
@SetUp fun setup(){
subject = TestSubject()
}
@Test fun test(){
subject.method()
}
}
主構造器
不能包含任何程式碼,需要在
init
中初始化
class Person constructor(firstName:String){
init{
//...
}
}
//類的引數,可以在建構函式中
class People(val firstName:String, val lastName:String){
//...
}
次建構函式
class Person{ //如果沒有主建構函式,則每個次建構函式,可能需要依次呼叫代理 //類似與Java中的多個建構函式,內部依次呼叫 constructor(parent:Person){ parent.children.add(this) } } //沒有顯式的宣告建構函式的話,會有一個預設的public的建構函式,類似於Java
抽象類
abstract
關鍵字//可繼承 open class Base{ open fun f(){} } abstract class Sub:Base{ override abstract fun f() }
巢狀類
class Outer{ private val bar:Int = 1 class Nested{ fun foo() = 2 } } //呼叫巢狀 fun main(args:Array<String>){ val demo = Outer.Nested().foo()//呼叫格式,Outer.Nested().foo() println(demo) }
內部類
inner
關鍵字//內部類會擁有外部類的引用,不同於巢狀類 class Outer{ private val bar:Int = 1 var v = "member property" 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) }
匿名內部類
class Test{ var v = "abc" fun setInterFace(test:TestInterFace){ test.test() } } //interface interface TestInterFace{ fun test() } //呼叫 fun main(args:Array<String>){ var test = Test() //匿名內部類 test.setInterFace(obj:TestInterFace{ override fun test(){ //... } }) }
類的修飾符
- 屬性修飾符
symbol | description |
---|---|
abstract | 抽象 |
final | 不可繼承,kotlin的類,預設都是final |
enum | 列舉 |
open | 可繼承 |
annotation | 註解類 |
- 許可權修飾符
symbol | description |
---|---|
private | 私有,同一檔案內可見 |
protect | 同一檔案內,或子類 可見 |
public | 公開 |
internal | 同一模組中可見 |
六、繼承
kotlin中所有類的繼承,都是Any
類,類似於Java中的Object
類
//Any類中含有以下三個函式
equals()
hashCode()
toString()
//open宣告可被繼承
open class Base(p:Int)
class Sub(p:Int):Base(p:Int)
- 繼承的建構函式
class Student:Person{
//子類沒有主建構函式,則需要在次建構函式中,super父類的建構函式
constructor(ctx:Context):super(ctx){}
}
//若是子類有主建構函式,基類也應一致,且在子類繼承時候,初始化
open class Person(var name:String,var age:Int){}
//此處,:Person被初始化了name,age
class Student(name:String,age:Int):Person(name,age){
//...
}
- 重寫函式,屬性
基類函式,預設final,需要open的方可重寫,重寫用override標記
若繼承的基類,或介面中,多個基類或介面含有相同函式,則需要複寫,且顯示super.fun() 呼叫
屬性的重寫
open class Foo{ open val x:Int get{...} } class Bar:Foo(){ override val x : Int = ... } //屬性的重寫,需要相容,var可以重寫val,但是val不能重寫var //可以在建構函式中直接宣告 重寫 interface Foo{ val count:Int } class Bar1(override val count:Int):Foo class Bar2:Foo{ override var count:Int = 0 }
七、介面
類似Java8,interface
關鍵字,可有預設實現
interface MyInterface{
fun bar()
fun foo(){
println("foo")
}
}
一個類可以實現多個介面
介面的屬性,抽象無值,必須複寫
interface MyInterface{ var name:String } class MyImpl :MyInterface{ override var name:String = "Kotlin" }
八、擴充套件
Kotlin
支援對類的靜態擴充套件,不需要繼承,或者裝飾器模式設計
- 擴充套件函式
//格式,擴充套件物件、擴充套件函式、引數(Nullable)
fun receiverType.functionName(params){
body
}
//示例:
class User(var name:String)
//新增一個print函式給User類
fun User.Print(){
print("name:$name")
}
//呼叫
fun main(args:Array<String>){
var user = User("kotlin")
user.Print()//擴充套件新增的函式
}
靜態擴充套件的,擴充套件到那個類,就是屬於那個類/介面,而不會是它的實現/子類。
屬性也可被擴充套件,但不能初始化。
類內部擁有的其他物件,也可以被擴充套件
class MyClass{ companion object{}//Companion的物件 } fun MyClass.Companion.foo(){ print("擴充套件了類的擁有的物件") }
擴充套件作用域
package foo.bar fun Baz.goo(){...} //其他包使用 package com.example.usage //import 匯入 import foo.bar.goo fun usage(baz:Baz){ //就能原本不能訪問的 baz.goo() }
擴充套件成員,分發接收者、擴充套件接收者
class D{ fun duck() print("Dark duck") } class C { fun cook()print("Cook food") //擴充套件D的函式 fun D.ext(){ duck()//能夠呼叫D的函式 cook()//也能呼叫C的函式 } //提供呼叫擴充套件函式的方法 fun callDext(d:D){ d.ext()//呼叫擴充套件函式 } } //示例 fun main(args:Array<String>){ val c:C = C()//c被稱為分發接收者 val d:D = D()//d被稱為擴充套件接收者 c.callDext(d) }
九、資料類&密封類
kotlin
可建立一個只包含資料的類,data
關鍵字
- 資料類主建構函式,至少一個引數
- 引數標識val或var
- 資料類不能abstract、open、sealed或inner
- 不能繼承其他基類,可以多介面實現
附帶一些函式equals/hashCode;toString,copy
等
複製:
data class User(val name:String, val age:Int) fun main(args:Array<String>){ val jack = User(name="Jack",age = 1) val olderJack = jack.copy(age = 2) print(jack) print(olderJack) } //解構宣告 val jane = User("Jane",27) val(name,age) = jane println("$name,$age years of age") //輸出結果:Jane,27 years of age
標準資料類
Pair
和Triple
,但是建議自定義資料類使用密封類
sealed
關鍵字,用於限定類中的值的型別,而不是任意型別密封類可有子類,但必須在其中內嵌
sealed
不能修飾interface、abstract類,會警告,但不會編譯錯誤
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
}
十、泛型
class Box<T>(t:T){
var value = t
}
//示例,例項化時候,需要具體引數型別
val box:Box<Int> = Box<Int>(1)
//或
val box = Box(1)
//函式
fun <T> boxIn(value:T) = Box(value)
val box2 = boxIn<Int>(1)
val box3 = boxIn(1)//自動推測型別
//可推斷的型別,可不申明
fun main(args: Array<String>) {
val age = 23
val name = "runoob"
val bool = true
doPrintln(age) // 整型
doPrintln(name) // 字串
doPrintln(bool) // 布林型
}
fun <T> doPrintln(content: T) {
//when內做了型別判斷
when (content) {
is Int -> println("整型數字為 $content")
is String -> println("字串轉換為大寫:${content.toUpperCase()}")
else -> println("T 不是整型,也不是字串")
}
}
泛型約束,類似於Java中的T extends ABC之類的
fun <T : Comparable<T>> sort(list:List<T>){
//...
}
//多個上限約束,使用where
fun <T> cloneWhenGreater(list:List<T>,threshold:T):List<T> where T:Comparale,Cloneable{
//...
}
型變
kotlin沒有萬用字元,有
declaration-site variance
和type projections
- in 消費,入參,非出參
- out 生產,作為出參,而不能入參(即,返回型別,而非傳入型別)
//定義支援協變的類 class Test<out A>(val a:A){ fun foo():A{ return a } } // fun main(args:Array<String>{ var strCo:Test<String> = Test("a") var anyCo:Test<Any> = Test<Any>("b") anyCo = strCo println(anyCo.foo())//輸出結果為 “a” }
//定義一個支援逆變的類 class Test<in A>(a:A){ fun foo(a:A){ //... } } // fun main(args:Array<String>){ var strDCo = Test("a") var anyDCo = Test<Any>("b") strDCo = anyDCo }
星號投射
Function<*,Sting>,表示Function<in Nothing,String> Function<Int,*> ,表示Function<Int,out Any?> Function< , > ,表示Function<in Nothing,out Any?>
十一、列舉類
enum class Color{
RED,BLACK,BLUE,GREEN,YEELOW,WHITE
}
//或
enum class Color(val rgb:Int){
RED(0xFF0000),
GREEN(0x00FF00)
}
//匿名內部類形式
enum class ProtocolState{
WAITING{
override fun signal() = TALKING
},
TALKING{
override fun signal()= WAITING
};
abstract fun signal():ProtocolState
}
列舉中,成員用;
分隔
enum class Color{
RED,BLACK,BLUE,GREEN,WHITE
}
//test
fun main(args: Array<String>) {
var color:Color=Color.BLUE
println(Color.values())
println(Color.valueOf("RED"))
println(color.name)
println(color.ordinal)
}
十二、物件表示式&宣告
//匿名類物件
window.addMouseListener(obj:MouseAdapter(){
override fun mouseClicked(e:MouseEvent){
...
}
})
//或繼承基類,介面的物件
val ab:A = obj:A(1),B{//A 是基類,B是介面,ab宣告一個A型別物件,= 一個A的子類且實現了B介面的物件
override val y = 15
}
//越過類的定義
fun main(args: Array<String>) {
val site = object {
var name: String = "菜鳥教程"
var url: String = "www.runoob.com"
}
println(site.name)
println(site.url)
}
//建構函式,共有的話,則public,
class C {
// 私有函式,所以其返回型別是匿名物件型別
private fun foo() = object {
val x: String = "x"
}
// 公有函式,所以其返回型別是 Any
fun publicFoo() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x // 沒問題
val x2 = publicFoo().x // 錯誤:未能解析的引用“x”
}
}
object
關鍵字,宣告物件
object Site {
var url:String = ""
val name: String = "Google Cloud"
}
fun main(args: Array<String>) {
var s1 = Site
var s2 = Site
s1.url = "www.google.com"
println(s1.url)
println(s2.url)
}
//兩個輸出,都是同一個url
class Site {
var name = "google cloud"
object DeskTop{
var url = "www.google.com"
fun showName(){
print{"desk legs $name"} // 錯誤,不能訪問到外部類的方法和變數
}
}
}
fun main(args: Array<String>) {
var site = Site()
site.DeskTop.url // 錯誤,不能通過外部類的例項訪問到該物件
Site.DeskTop.url // 正確
}
類內部的物件宣告,可以用companion
關鍵字標記,構成伴生,一個類只有一個伴生物件,即companion
只能出現一次
class MyClass{
companion object Factory{
fun create():MyClas = MyClass()
}
}
//access
val instance = MyClass.create()
十三、委託
kotlin直接支援委託模式,即一個類中的方法,呼叫另一個類的函式實現by
關鍵字
- 類委託
// 建立介面
interface Base {
fun print()
}
// 實現此介面的被委託的類
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 通過關鍵字 by 建立委託類,由Base的實現類,具體操作
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 輸出 10
}
屬性委託
某個屬性,是由其他類代理實現
var/val name:Type by 表示式
- 定義為拖類,包含getValue、setValue函式,thisRef、prop
// 定義包含屬性委託的類 class Example { var p: String by Delegate() } // 委託的類 class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, 這裡委託了 ${property.name} 屬性" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$thisRef 的 ${property.name} 屬性賦值為 $value") } } fun main(args: Array<String>) { val e = Example() println(e.p) // 訪問該屬性,呼叫 getValue() 函式 e.p = "google kotlin" // 呼叫 setValue() 函式 println(e.p) }
標準委託
- 延遲屬性Lazy
val lazyValue: String by lazy { println("computed!") // 第一次呼叫輸出,第二次呼叫不執行 "Hello" } fun main(args: Array<String>) { println(lazyValue) // 第一次執行,執行兩次輸出表達式 println(lazyValue) // 第二次執行,只輸出返回值 }
- Observable
class User { var name: String by Delegates.observable("初始值") { prop, old, new -> println("舊值:$old -> 新值:$new") } } fun main(args: Array<String>) { val user = User() user.name = "第一次賦值" user.name = "第二次賦值" } //執行結果 舊值:初始值 -> 新值:第一次賦值 舊值:第一次賦值 -> 新值:第二次賦值
- map 對映
class Site(val map: Map<String, Any?>) { val name: String by map val url: String by map } fun main(args: Array<String>) { // 建構函式接受一個對映引數 val site = Site(mapOf( "name" to "Google Cloud", "url" to "www.google.com" )) // 讀取對映值 println(site.name) println(site.url) } //結果 Google Cloud www.google.com
- Not Null
class Foo { var notNullBar: String by Delegates.notNull<String>() } //若是不賦值,就print訪問,則會拋異常 foo.notNullBar = "bar" println(foo.notNullBar)
- 區域性委託屬性
尷尬,粗略的看一遍,真的有點累,以後慢慢充實….