Kotlin是一種靜態語言,該語言可在Java虛擬機(JVM)上運行,並可被編譯成JavaScript源程序,Kotlin語言在使用時可直接調用Java類庫,並具有與Java程序進行互操作的能力。
Kotlin語言具有簡潔、安全、支持跨語言互操作等技術特徵,本文主要從基本語法、方法與Lambda表達式、類與對象、泛型等反面進行基本說明,偏重講與Java語言不同的地方,建議有Java語言基礎的同學學習。
本篇內容有所精簡,閱讀時如果感到有所跨越,可以閱讀第二篇Kotlin語言入門學習(二)進行補充
一、基本語法
1.1基本數據類型
Kotlin語言支持的基本數據類型包含數字型、布爾型、字符和數組。
1.1.1數字:
Kotlin內置的數字類型有:Double、Float、Long、Int、Short、Byte
對於十六進制的數字,格式爲0x數字,如:0xFF
對於二進制整數,格式爲0b數字,如:0b001
Kotlin支持以下劃線來分割數字,如:1_2345_6789
1.1.2類型轉換
Kotlin程序中,位數短的數據類型不能直接轉換成位數長的數據類型(與Java有區別)
不同類型的數字,可以使用toXXX()進行轉換,如:轉換爲字節型10.toByte(),轉換爲字符123.toChar()
1.1.3數學運算
除基本的+,-,*,/,%(求模),Kotlin語言中的*運算符還可作爲傳值符號,支持將一個數組賦值給一個包含可變長輸入參數的方法
位運算
shl(bits),類似於Java的<<運算
shr(bits),類似於Java的>>運算
ushl(bits),類似於Java的<<<運算
ushr(bits),類似於Java的>>>運算
and(bits),位上的和
or(bits),位上的或
xor(bits),位上的異或
inv(),位上取反
1.1.4字符
Kotlin語言中,字符使用類型聲明符Char進行說明,字符數據必須使用單引號來表示
var c: Char = 'a' 表示將字符'a'賦值給變量c
1.1.5數組
Kotlin中創建數組使用arrayOf或arrayOfNulls方法
var s1: Array<String> = arrayOf("a","b","c")
var s2: Array<String?> = arrayOfNulls<String>(2) //s2爲一個長度爲2的空字符串數組
Kotlin程序在聲明變量時,如果變量類型後使用了符號"?",則表示該變量可爲空,否則,表示不能爲空
Kotlin中還定義了特定數組類,如ByteArray、ShortArray等
數組初始化可基於“工廠函數”實現
var ins = Array(5,{i->i+1})
Array語句第一個參數5表示數組長度,第二個參數{i->i+1}爲一個工廠函數,表示將元素索引值加1,並將值設置爲數組中對應元素的值,以上數組元素爲1,2,3,4,5
1.1.6字符串
Kotlin中的字符串可使用模板表達式,基本格式爲$標識名
val i = 9
val s = "$i is in the string" //$i爲一個模板表達式
val s1 = "\'$s\'.length is ${s.length}" //$s和${s.length}爲模板表達式
上述語句執行後,$i顯示9,${s.length}顯示18
1.1.7數據類型的檢查和轉換
數據類型檢查使用操作符is或!is,如:檢查變量a是否爲一個整型時,使用if(a is Int){...},空值的比較用==
類型轉換使用操作符as,當變量a爲null時,var b:Int = a as Int爲不安全轉換,可使用var b:Int? = a as Int?進行控制。另外,可使用as?進行安全轉換
1.2程序的控制結構
Kotlin程序中的控制結構包含if,when,for,while,其中,if和when可作爲表達式直接使用
var value = if (a>b) {a} else {b}
when結構
when(變量){
值1 ->語句1
值n ->語句n
}
類似於Java中的switch...case語句
1.3集合類型
除數組結構外,Kotlin中的集合類型包含列表List,集合Set,字典Map等;Kotlin中,集合類型分爲可修改和只讀兩種
Kotlin中,只讀列表基於List<T>接口定義,可修改列表基於MutableList<T>定義;類似,Set<T>爲只讀集合,MutableMap<K,V>爲可修改字典等
初始化集合類型時,推薦直接調用系統提供的標準方法:listOf,mutableListOf,setOf,mutabeSetOf,mapOf,mutableMapOf等。複製一個集合類型的數據,可使用的方法爲toMap,toList,toSet等
1.4數值範圍
Kotlin可直接使用數值表達式:..(兩個點),如:1..10表示範圍1至10(整數)
在for循環中,使用for(i in 1..10)或for(i in 10 downto 1)
也可控制步長,for(i in 1..10 step 2),表示從1開始,每次前進2步,至10終止
不需要使用某個範圍的終止值時,可使用until,如:for(i in 1 until 10) 表示數值範圍從1開始,至9終止
1.5等式
Kotlin可使用兩種等式運算符:===和==
==用於值或結構相等關係的判斷,===用於應用對象相等關係的判斷
1.6操作符
除常規操作符外,Kotlin還有在範圍中進行查詢或遍歷的in操作符:in和!in
二、方法與Lambda表達式
2.1方法
fun 方法名稱(參數列表): 返回值類型{
//執行語句
return 返回值
}
參數列表中參數聲明的基本格式爲“參數名:參數類型”。當方法沒有返回值時,返回值類型可省略
當方法中的程序相對簡單,僅包含計算表達式,並將計算結果返回時,可使用以下簡化格式:
fun 方法名稱(參數列表) = 計算表達式,如:fun add(a:Int, b:Int = 10) = a+b //b指定了默認值
Kotlin中的一個方法只能包含一個變長參數,而且該變長參數只能位於方法定義中參數列表的末尾。變長參數需使用關鍵字vararg,如:vararg vs:Array<Int>表示vs是一個接收多個整型的參數
另外,當某一方法定義時滿足了3個條件:定義時使用了infix關鍵字,方法只有一個輸入參數,方法是類成員(或類的擴展方法),則該方法可以中綴方式使用,基本結構爲:類實例 方法名 參數。
如:下述程序運行的結果會在輸出窗口中顯示“string-sub”:
infix fun String.extends(str:String):String{
return this.toString() + str
}
fun main(args:Array<String>){
val s = "string"
val ss = s extends "-stub"
println(ss)
}
2.2方法的聲明與使用
一個方法的使用是通過方法名來實現的。在使用一個方法時,還需要指定該方法的輸入參數,如:add(2,3)爲add函數的一種使用。
下列程序展示了本地方法的聲明和使用(程序中,increase是本地方法,程序運行結果爲6):
fun add(a:Int,b:Int):Int{
fun increase(c:Int):Int{
return c+1
}
return increase(a+b)
}
fun main(args:Arrat<String>){
println(add(2,3))
}
2.3Lambda表達式和高階方法
Lambda表達式是一種匿名方法的表示方式。Lambda表達式一般使用箭頭來表示一個運算操作,該操作分爲3個部分:箭頭,箭頭左邊,箭頭右邊。其中,箭頭用於表示一個映射,箭頭左邊是映射的輸入參數列表,箭頭右邊爲映射的輸出。
如:{x:Int,y:Int ->x+y},該運算有兩個輸入整型參數x和y,而運算結果(輸出)爲x+y。此外,Lambda表達式在聲明時需要使用花括號,即{}
Lambda可被用於賦值給一個常量或變量,如:val add = { x:Float,y:Float -> x+y},這樣,add實際上可被看作一個方法,該方法有兩個輸入參數x和y,輸出x+y,使用時爲add(0.1f,0.2f)。
方法聲明也類似,箭頭左邊是輸入參數類型列表,右側爲輸出類型,如:(Int,Float) ->Float,該表達式表示方法的兩個輸入參數爲Int和Float,輸出參數類型爲Float。方法不使用花括號
方法類型可看成是一種數據類型,並用於聲明常量或變量。如:val calc:(Int,Float) ->Float = {x:Int,y:Float ->x*y},此例中,calc實質上是一個類型爲(Int,Float) ->Float的運算,而具體的實現則被定義爲{x:Int,y:Float ->x*y}。
在Kotlin中,所謂高階方法是指方法中的參數是方法,或者方法的返回值是方法。如,在下列程序中,calc1和calc2爲高階方法:
fun main(args:Array<String>){
fun calc1(n:Int,f:(Int) ->Int):Int{
return f(n)
}
println(calc1(10,{it+1}))
println(calc1(10,{i ->i-1}))
fun calc2(n:Int,fn:Float,f:(Int,Float) ->Float):Float{
return f(n,fn)
}
println(calc2(10,0.2f,{i:Int,f1:Float ->i*f1}))
}
2.4匿名方法和閉包
除了Lambda表達式可用來定義匿名方法外,匿名方法還可直接被定義,如:
fun (x:Int,y:Float):Float = x*y
fun(x:Int,y:Float):Float{
return x*y
}
匿名方法和Lambda表達式可訪問它們的閉包,也就是說可訪問外部範圍的變量。下列程序展示一種簡單的閉包訪問(結果爲101):
fun main(args:Array<String>){
var n = 100
fun p(){
n = n+1
}
p()
println(n)
}
上述程序中n可被看成一個main方法範圍內的公共變量,而方法p與n在同一個方法內,所以該方法可以直接訪問n,並進行計算。基於上述程序,一個閉包還可以這樣使用:
val print = println("testing")
fun main(args:Array<String>){
}
上述程序將print定義爲一個語句,然後再main中直接調用,程序運行結束,輸出窗口會輸出“testing”。程序中print和main在同一個文件內,所以print可被視爲文件內的公共變量,而main方法可直接使用print。
三、類與對象
3.1類的聲明
與Java類似,使用class關鍵字
屬性聲明:var用於定義變量,val用於定義常量
如:var 屬性名:屬性類型 = ...
3.2類的構建器
Kotlin中的類有兩種構建器:主構建器和非主構建器
主構建器使用constructor關鍵字說明
class 類名 constructor(參數列表){
var 屬性名:屬性類型 = ...
...
val 屬性名:屬性類型 = ...
}
若主構建器包含註釋或訪問權限說明,則關鍵字constructor不可省略,如:
class Machine public @Inject constructor(type:String){
...
}
否則,可省略關鍵字constructor
非主構建器
非主構建器的定義位於類定義的內部,使用關鍵字constructor說明:
class 類名{
constructor(參數列表){
}
}
若一個類存在主構建器,在定義非主構建器時,該構建器要麼需要調用構建器,要麼需要調用另一個已定義的非主構建器。如:
class Machine(t:String,n:Int){
val type = t
val sum = n
//非主構建器1
constructor(t:String):this(t,0)
//非主構建器2
constructor(n:Int):this("equipment",n)
//非主構建器3
constructor():this(0)
}
上述程序包含1個主構建器和3個非主構建器,1和2通過this操作符調用主構建器來完成初始化工作,3調用非主構建器2完成初始化工作
若在程序創建時還無法確定屬性的具體值時,相關屬性需要使用lateinit進行說明,且lateinit所修飾的變量只能是可變更變量,如:
lateinit var txt:TextView
3.3設值器和取值器(setter和getter)
示例:
class SimpleClass(str:String){
var att1 = str
var att2:String? = null
get(){ //自定義取值器
if(field == null){ //field關鍵字指代一個屬性實例,此處指att2
return "an attribute"
}else{
return field
}
}
set(s:String?){ //自定義設值器
field = "att2 again"
}
}
使用
fun main(args:Array<String>){
var cls = SimpleClass("a class")
println(cls.att1)
cls.att1 = "a value"
println(cls.att1)
println(cls.att2)
cls.arr2 = "att"
println(cls.att2)
}
程序運行結果如下:
a class
a value
an attribute
att2 again
3.4類的繼承
Kotlin中允許被繼承的類必須使用open關鍵字來進行說明,示例:
open class 父類名(參數列表){
}
繼承結構:
...子類名(...):父類名(...)
示例:
open class SimpleClass(str:String){
var att1 = str
}
class MyClass(s:String):SimpleClass(s) //子類MyClass沒有程序內容,故省略程序體{...}
在繼承中,父類沒有使用主構建器,則子類可在聲明時調用父類的非主構建器,子類也可以在自己的非主構建器定義時調用父類中的非主構建器(使用super),如:
open class SimpleClass{
var att1:String
constructor(s:String){
att1 = s
}
constructor(n:Int){
att1 = n.toString()
}
}
class MyClass(n:Int):SimpleClass(n)
class MyClass2:SimpleClass{
constructor(s:String):super(s)
}
MyClass使用父類名調用父類的非主構建器,MyClass2使用super調用父類的非主構建器
方法覆蓋:使用關鍵字override
父類中有方法open fun service():String{
return att1
}
則子類覆寫該方法時:
override fun service():String{
return "service"
}
Kotlin中屬性也可以覆蓋,同樣使用關鍵字open 和 override ,另外,var可覆蓋val,但val不能覆蓋var
3.5抽象類與接口
與Java類似,抽象類使用abstract關鍵字,接口使用interface關鍵字
同樣,子類覆寫方法時也要使用override關鍵字
Kotlin中允許使用抽象方法覆蓋非抽象方法
如果抽象方法提供了默認實現,則子類可以不覆寫該方法
3.6程序對象的可見性說明
Kotlin中可見性說明符有public、internal、protected、private,默認爲public
其中,package包使用internal表示模塊(構建工具指定的代碼單元)內可見,protected爲不可使用
類與接口使用internal時,模塊內的程序可見,使用protected時,本類和子類可見
3.7擴展
Kotlin支持通過聲明對類進行直接擴展,示例:
class MyClass(s:String){ //待擴展的一個類
var att = s
fun show(){
println(att)
}
}
val MyClass.att1:String //待擴展屬性
get() ="att1"
fun MyClass.service(){ //擴展方法
println("working with:"+att1)
this.show()
}
fun main(args:Array<String>){
val c = MyClass("cls")
c.show()
c.service()
}
另外,也可以在不同類中進行擴展,如:在類B中對類A進行擴展
3.8數據類
數據類是一個持有數據的簡單類,定義的格式爲data class 類名(參數列表),編譯器會爲數據類增加以下內容:
equals方法,hasCode方法,toString方法,copy方法,componentN方法
數據類不能是abstract,open,sealed(密封類,限制繼承),inner類型的類
3.9拆分結構
拆分結構的基本結構爲:(變量或常量名,變量或常量名,...),下列程序中,一個Object對象中的數據項被分別設置到a,b,c變量中:
data class Object(var it1:String,var it2:Int,var it3:Float)
fun main(args:Array<String>){
var obj = Object("item",1,0.1f)
var (a,b,c) = obj
println(a+":"+b+":"+c)
}
四、泛型、對象表達式和代理
4.1泛型
泛型在使用時要加註符號<>,並在符號內設置泛型參數,一般用大寫字母表示,如:<T>
Kotlin泛型在聲明時可使用關鍵字out 和 in
當定義類的泛型聲明中使用out,則帶有指定類型的對象可賦值給帶有該指定類型的父類型的變量或常量
當定義類的泛型聲明中使用in,則帶有指定類型的對象可賦值給帶有該指定類型的子類型的變量或常量
4.2對象表達式
對象表達式用於聲明一個匿名對象
基本使用場景:
方法名(object:接口名){
接口中方法的定義
}
或:
方法名(object:抽象類名()){
抽象類中方法的定義
}
對象表達式還可用於直接定義簡單數據結構,如:
val 常量名 = object {
變量聲明列表
}
示例:
val t = object{
val a = 100
}
4.3對象聲明
基本語法:
object 對象名稱{
屬性聲明列表
...
方法聲明
}
對象聲明可基於特定父類,語法結構爲:
object 對象名稱:父類名稱(參數列表){
屬性聲明列表
...
方法聲明
}
4.4類代理
類代理可以理解爲:訪問組件通過中間組件進行對服務組件的訪問,使用關鍵字by
示例:
interface Inter{ //可訪問接口
fun service()
}
class SimpleClass1:Inter{ //服務組件1
override fun service(){
println("simple class one")
}
}
class SimpleClass2:Inter{ //服務組件2
override fun service(){
println("simple class two")
}
}
class Agent(i:Inter):Inter by i //代理組件
fun main(args:Array<String>){
var c:Inter = SimpleClass1()
Agent(c).service() //通過代理組件訪問服務組件1
c = SimpleClass2()
Agent(c).service() //通過代理組件訪問服務組件2
}
代理屬性:語法爲:var(或val) 變量名:變量類型 by 代理類名稱()
Kotlin標準類庫中還預定義了很多可以直接使用的代理工具,如:lazy和observable
Kotlin中的反省技術是針對程序或程序的運行實例進行分析和解釋的技術,反省實現的基礎是引用技術
如:查看類的註解,可使用反省實現
本文參考:基於Kotin的Android應用程序開發(薛崗)