一、Kotlin基礎 1.數據類型聲明 在Kotlin中要定義一個變數需要使用var關鍵字 //定義了一個可以修改的Int類型變數 var number = 39 如果要定義一個常量可以使用val關鍵字,等價於Java的final關鍵字. val name = "miku" //給val定義的常量再 ...
一、Kotlin基礎
1.數據類型聲明
在Kotlin中要定義一個變數需要使用var關鍵字
//定義了一個可以修改的Int類型變數
var number = 39
如果要定義一個常量可以使用val關鍵字,等價於Java的final關鍵字.
val name = "miku"
//給val定義的常量再次賦值就會提示錯誤
name = "ミク"
在Kotlin中要聲明一個數據類型必須要使用var或者val來定義.
2.數據類型
Kotlin的數據類型分為基本數據類型和引用數據類型.
基本數據類型:Boolean、Number、Char
引用類型:可空類型、Object、數組類型
Kotlin中的Number類型泛指所有跟數字有關的類型,int、float、double、long
//java.lang.Integr Int--->int
var intNumber:Number = 39
//java.lang.Float Float--->float
var floatNumber:Number = 39.0f
//java.lang.Double Double--->double
var doubleNumber:Number = 39.000000000000000
//java.lang.Long Long--->long
var longNumber:Number = 39L
2.1.Kotlin預定義的基本數據類型
var intType:Int = 39 //int
var shortType:Short = 39 //short
var byteType:Byte = 0 //byte
var boolType:Boolean = true //boolean
var stringType:String = "miku" //String
var floatType:Float = 39f //float
var doubleType:Double = 39.0000000000 //double
var longType:Long = 39L //long
var numberType:Number = 39 //Integr、Float、Double、Long
var charType:Char = '0' //char
字元串拼接,通過${}直接引用變數,不再需要引號 + 引號的方式
val name = "komine"
val text = "私の名前は${name}と申します。"
print(text)
2.2.元組
元組是一個固定長度,且不能被修改的數據結構
//二階元組
var person1 = Pair("komin",16)
//三階元組
var person2 = Triple("komine",18,"女")
person2.first //第一個元素的值
person2.second //第二個元素的值
person2.third //第三個元素的值
Kotlin的元組其實就是一個泛型對象的封裝.通過反編譯生成的.class文件可以看出,二階元組其實是對應Pair類,
三階元組其實是對應Triple類. first、second、third分別對應getFirst()、getSecond()、getThrid()方法.
如果用Java來實現的話差不多就是下麵這個樣子,可以看到,元組不過是Kotlin在源碼階段提供的一種更方便的語法而已.
public class MyPair<First,Second> {
private final First mFirstValue;
private final Second mSecond;
public MyPair(First first, Second second){
this.mFirstValue = first;
this.mSecond = second;
}
public First getFirst() {
return mFirstValue;
}
public Second getSecond() {
return mSecond;
}
}
2.3.可空類型
Kotlin定義了一種可以為空的數據類型,只有聲明為可空類型的變數才能將null賦值於它.預設聲明的類型全部都是非空類型.
var str:String? = null //在類型的後面添加?表示該類型可空
print(str?.length) //通過?.方式來訪問可空類型的成員方法或變數,如果str是null則後面的調用會返回null,不會報錯
//如果你明確知道,str在某個時候肯定不為空,可以通過!!操作該變數,但如果str為空則會拋出空指針異常
print(str!!.length)
2.4.數組
在Kotlin中定義一個數組非常簡單.調用arrayOf()即可創建一個數組.
var names = arrayOf("miku","rin","luka")
var numbers = arrayOf(16,14,17)
//跟Java一樣,也可以通過[下標]的方式訪問數組的元素
print(names[0])
//創建一個指定長度的數組,值為null
var fixedArray = arrayOfNulls<Int>(39)
//創建一個空數組
var emptyArray = emptyArray<Int>()
2.5.集合
Kotlin的集合分為可變集合和不可變集合.可變集合都是由Mutable開頭的.
可變集合
//MutableList集合,該集合可以包含相同元素
var mutableList = mutableListOf("miku","rin","luka")
//鍵值對,包含Key和Value的集合
var mutableMap = mutableMapOf<String,String>()
var linkedHashMap = linkedMapOf<String,String>() //等價mutableMap
//MutableSet集合,該集合不會出現相同的元素,如果集合已經包含某個值,添加的時候會忽略添加操作
var mutableSet = mutableSetOf<Object>()
var linkedSet = linkedSetOf<String>() //等價mutableSet
不可變集合
//List集合,無法進行添加操作
var list = listOf("miku","rin","luka")
//Map集合,無法進行添加操作
var map = mapOf(Pair("miku",16))
var linkedMap =
//Set集合,無法進行添加操作
var set = setOf<Int>(0,1,2,3,4,5,6,7,8,9)
2.5.1擴展方法
toList() 返回一個不可變List集合
toMap() 返回一個不可變的Map集合
toSet() 返回一個不可變的Set集合
2.5.2集合操作
類似.Net中的Linq查詢
any 判斷集合中是否有滿足條件的元素
var mutableList = mutableListOf("miku","rin","luka")
val result:Boolean = mutableList.any(){
it == "miku" //有一個元素的值為miku
}
all 判斷集合中的所有元素是都否滿足條件
var mutableList = mutableListOf("miku","rin","luka")
val result:Boolean = mutableList.all(){
it.isNotEmpty() //元素的長度大於0
}
none 判斷集合中的所有元素是否都不滿足條件,滿足則返回true
var mutableList = mutableListOf("miku","rin","luka")
val result:Boolean = mutableList.none(){
it.isNotEmpty() //元素的長度等於0 false
}
count 返回滿足條件的元素個數,類似sql中的select count(*) from table where xx = xx
var mutableList = mutableListOf("miku","rin","luka")
val result: Int = mutableList.count {
it == "miku" //返回集合中元素值為miku的元素個數
}
reduce 從第一個元素到最後一個元素的累加
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result: Int = numbers.reduce() {
sum: Int, i -> sum + i //sum:聲明一個用於接收累加結果的變數 i->:迴圈每一個元素,sum + i:累加每個元素到sum
}
reduceRight 從最後一個元素到第一個元素的累加
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result: Int = numbers.reduceRight() {
sum: Int, i -> sum + i
}
fold 跟reduce類似,不過可以設置初始化,從初始值開始累加
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result: Int = numbers.fold(10){
sum, i -> sum + i
}
foldRight...
sumOf 返回集合中所有元素的總和,該集合的元素必須是Number類型
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.sumOf{
it
}
dropWhile 去除滿足條件的元素,直到不滿足為止,返回剩餘元素的集合
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.dropWhile {
it < 5
}
filter 過濾不滿足條件的元素,返回滿足元素的新集合
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.filter {
it < 5
}
filterNot 過濾滿足條件的元素,返回不滿足元素的新集合
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.filterNot {
it < 5
}
take 返回從第一個元素開始的N個元素
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.take(5) //{1,2,3,4,5}
takeLast 返回從最後一個元素開始的N個元素
...
takeWhile 返回從第一個元素開始符合給定條件的元素
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.takeWhile {
it > 0
}
drop 返回去掉N個元素之後的列表
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.drop(1)
dropLastWhile 返回從最後一個元素開始,去掉滿足條件的元素
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.dropLastWhile {
it > 5
}
slice 保留指定下標對應的元素
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.slice(listOf(1,2,3))
map 將集合中的元素通過某種方法轉換後,返回一個新集合
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
val result = numbers.map {
val diff = 10
it * diff
}
後續更新...
2.6.類型轉換
Kotlin通過as關鍵字將一個類型轉換為另一個類型.
var numberType:Number = 39
var intType:Int = numberType as Int
Kotlin可以通過 is 關鍵字自動完成裝箱的操作.
open class GameConsole{
}
class PS4 : GameConsole() {
}
class NintendoSwitch : GameConsole() {
public val version = "10.0"
}
fun main(args: Array<String>) {
val gameConsoles = arrayOf(PS4(),NintendoSwitch())
val gameConsole = gameConsoles[1]
//會自動完成裝箱操作,在當前調用範圍內將gameConsole識別NitendoSwitch,可以直接訪問對象的成員,不需要手動轉換
if(gameConsole is NintendoSwitch){
print(gameConsole.version)
}
}
3.運算符
Kotlin特有的運算符有===、!==
3.1.恆等和非恆等
判斷兩個對象之間的地址是否相等
var obj1 = Object()
var obj2 = Object()
if(obj1 === obj2){
println("恆等")
}
if(obj1 !== obj2){
println("非恆等")
}
3.2.位運算
只有Int和Long類型可以使用.
val i:Int = 10
val j:Int = 20
val result = i shl j //有符號左移
result = i shr j //有符號右移
result = i ushr //無符號右移
result = i and j //按位與
result = i or j //按位或
result = i xor //按位異或
result = i inv j //按位取反
3.3.區間運算符
表示某個值是否在某個範圍或者集合之中.
//i >=0 && i <= 100
if(i in 0..100){
print(i)
}
數組或者集合是否包含某個值
val names = arrayOf("miku","rin","luka")
if("miku" in names){
print("包含")
}
4.條件語句
when是Kotlin提供類似Java中Switch的條件語句.它能做到Switch能做到的所有事,並且還提供了更方便的語法.
fun test(i:Int){
when(i){
// case 1
1 ->{
}
//case 2
2 -> {
}
//i >= 3 && i <= 9
in 3..9 ->{
}
//default
else ->{
}
}
}
when如果不提供參數也可以當作if elseif使用
5.迴圈語句
跟Java的使用差別不大,一般配合區間運算符in來更簡便的使用
val names = arrayOf("miku","rin","luka")
for (name in names){
println(name)
}
//遍曆數組或集合帶索引
for ((index,name) in names.withIndex()){
}
val i = 0
for (i in 0..99){ //i = 0;i <= 99;i++
println(i)
}
var i = 10
while (i > 0){
println(i)
i--
}
do {
i--
println(i)
}while (i > 0)
//foreach
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
numbers.forEach{ it ->
println(it)
}
//foreach 帶索引
val numbers = mutableListOf(1,2,3,4,5,6,7,8,9)
numbers.forEachIndexed{index, i ->
println("index[${index}] = $i")
}
6.函數
Kotlin中定義函數使用fun關鍵字.如果方法不需要返回值可以不寫,或者寫Unit
fun sum(number1:Int,number2:Int):Int{
return number1 + number2
}
6.1預設參數
Kotlin中添加了預設參數的支持,當一個方法指定了參數的預設值,則調用的時候可以不提供該參數的值.
要使用預設參數,在參數的後面添加=進行賦值操作.
fun sum(number1:Int,number2:Int = 0):Int{
return number1 + number2
}
6.2可變參數
和Java一樣,Kotlin也提供可變參數的支持,使用vararg關鍵字聲明可變參數
fun sum(vararg number:Int){
//number在使用的時候其實是一個數組類型的變數,可以調用數組的一些方法
}
6.3Lambda表達式
Lambda表達式可以看作是一個匿名的函數.
var execute:(Int,Int) -> Int = {x,y ->
x * y
}
println(execute(10,10))
如果函數只有一個參數時可以省略不寫,這個時候用it來表示
var execute:(String) -> String = {
it
}
println(execute("komine"))
6.4高階函數
Kotlin支持將函數作為參數或者返回值,包含這樣操作的函數稱為高階函數.將函數作為參數時使用雙冒號::來傳遞.
fun main(args: Array<String>) {
call(::method)
}
fun call(m:(number:Int) -> Int){
println(m(39))
}
fun method(number:Int):Int{
return number * number
}
也可以使用Lambda表達式來表示一個匿名參數.
call{number: Int -> return@call number * number }
Kotlin本身也提供了一些高階函數供我們使用,比如apply函數,在Android中初始化變數可以這樣寫.
var paint = Paint().apply {
this.isAntiAlias = true
this.color = Color.BLACK
this.style = Paint.Style.STROKE
this.strokeWidth = 10f
}
6.5內聯函數
Kotlin支持內聯函數,跟C++的內聯函數作用一致,因為函數的執行有壓棧和出棧的步驟,會帶來一定的開銷.
在將函數聲明為內聯函數的時候,在編譯的時候,編譯器會在所有調用函數的地方,將函數調用直接替換成函數體的內容.
一般來說,內聯函數中的嵌套邏輯不能太複雜,C++的內聯函數是否替換是由編譯器決定的,Kotlin會按照inline關鍵直接替換.
通過反編譯生成的class文件可以看到,內聯函數就是直接將有函數調用的地方,直接替換成函數體的內容.
fun main(args: Array<String>) {
test()
}
inline fun test(){
for (i in 0..99){
println(i)
}
for (i in 0..99){
println(i)
}
for (i in 0..99){
println(i)
}
for (i in 0..99){
println(i)
}
for (i in 0..99){
println(i)
}
for (i in 0..99){
println(i)
}
}
反編譯結果
如果函數的參數中有函數參數類型或者Lambda表達式,也可以使用noinline關鍵字指定不參數內聯的函數.
crossinline關鍵字待補充...
二、Kotlin進階
1.協程
協程是跑線上程上的產物,它擁有自己的棧記憶體和局部變數,被稱為輕量級Thread.它的內部實現是由編譯器來完成的.
在使用協程之前,需要添加依賴
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
簡單用法
GlobalScope.launch(context = Dispatchers.Default, start = CoroutineStart.DEFAULT) {
//延時1.5秒
delay(1500L)
println("當前線程:" + Thread.currentThread().name)
println("World")
}
println("Hello,")
println("當前線程:" + Thread.currentThread().name)
Thread.sleep(3000L)
context:協程的上下文,這裡設置的是CoroutineDispatch協程運行的線程調度器,它有四種線程模式:
Dispatchers.Default //預設
Dispatchers.IO //工作在其他線程
Dispatchers.Main //主線程
Dispatchers.Unconfined //不指定就是在當前線程,kotlinx.coroutines.DefaultExecutor
也可以自己創建一個協程上下文,這個上下文也可以理解為協程所運行在的線程.
val context = newSingleThreadContext("single")
start:啟動模式,預設是CoroutineStart.DEFAULT,就是創建之後就會啟動
CoroutineStart.DEFAULT
CoroutineStart.ATOMIC
CoroutineStart.UNDISPATCHED
CoroutineStart.LAZY 懶載入模式,它會返回一個Job對象,你可以手動開啟它.
val job = GlobalScope.launch(context = Dispatchers.Default, start = CoroutineStart.LAZY) {
//延時1秒
delay(1500L)
println("當前線程:" + Thread.currentThread().name)
println("World")
}
println("Hello,")
println("當前線程:" + Thread.currentThread().name)
job.start()
Thread.sleep(3000L)
GlobalScope.async 帶返回值
suspend fun main(args: Array<String>) {
val result = GlobalScope.async {
delay(1000L)
return@async "async"
}
println(result.await())
Thread.sleep(3000)
}
async會阻塞當前協程,會等待當前協程執行完畢,調用await()的函數需要使用suspend關鍵字修飾.
協程的掛起,suspend表示當前協程被掛起.
fun main(args: Array<String>) {
GlobalScope.launch {
//get方法是被suspend修飾的,表示調用時會將當前協程掛起
val str = get()
//會等待get()執行完畢才會繼續執行
printStr(str)
}
//防止進程結束
Thread.sleep(3000L)
}
suspend fun get():String{
println("get()正在執行..")
delay(1000)
return "data"
}
suspend fun printStr(str:String){
println(str)
}
協程之間也可以嵌套,調用await會阻塞外部協程,代碼還是會按順序運行
fun main(args: Array<String>) {
GlobalScope.launch {
val str = GlobalScope.async {
return@async get()
}.await()
GlobalScope.launch{
printStr(str)
}
}
//防止進程結束
Thread.sleep(3000L)
}
suspend fun get():String{
println("get()正在執行..")
delay(1000)
return "data"
}
suspend fun printStr(str:String){
println(str)
}
待補充...
2.面向對象
2.1類的聲明
和很多語言一樣,Kotlin使用class關鍵字來聲明一個類.
class Person{
}
2.1.1內部類
class Person{
//內部類的聲明使用inner關鍵字修飾
inner class Info{
}
}
要實例內部類需要先實例化主類
val person = Person("miku")
val info = person.Info()
2.1.2數據類
通過data class 修飾的類稱為數據類,數據類必須提供一個有參的構造函數.數據類一般不定義方法.
data class MyData(val height:Float,val weight:Float,val money:Float){
}
2.1.3枚舉類
跟Java的枚舉類使用基本相同.
enum class DirectionEnum{
East{
override fun move() {
//name:當前枚舉常量的名稱
//ordinal:當前枚舉常量的值
println("name$name")
println("value:$ordinal")
}
},
South{
override fun move() {
}
},
West{
override fun move() {
}
},
North{
override fun move() {
}
};
abstract fun move();
}
2.1.4密封類
emm...不知道怎麼用,後面補充
2.1.5抽象類
使用abstract關鍵字聲明一個抽象類
open abstract class Person(name:String, age:Int){
}
2.2構造函數
Kotlin的構造函數可以直接寫在類名的後面,稱為主構造函數,這樣定義的構造函數是沒有方法體的,要執行初始化操作
可以使用init代碼塊.
open class Person(name:String, age:Int){
init {
println("主構造函數執行...")
println("name:${name},age = $age")
}
}
2.2.1 副構造函數
通過constructor定義的構造函數稱為副構造函數,需要間接調用主構造函數進行初始化
fun main(args: Array<String>) {
val person = Person("miku")
}
open class Person(name:String, age:Int){
init {
println("主構造函數執行...")
println("name:${name},age = $age")
}
//副構造函數需要間接調用主構造函數通過this關鍵字
constructor(name:String) : this(name,16) {
println("副構造函數執行...")
}
}
2.3繼承
如果想讓一個類可以被其他類繼承,需要在類的聲明之前加上open關鍵字,使用:冒號來繼承
open class Person{
open fun take(){}
}
class Student : Person() {
}
如果子類想重寫父類定義的方法,該方法必須是open關鍵字修飾的方法,抽象方法的修飾符預設是open.
2.4靜態成員
Kotlin沒有提供static關鍵字,如果想實現Java那樣的靜態成員調用可以使用companion object代碼塊來定義靜態成員.
在Kotlin中稱為伴生對象,用伴生對象的成員來代替靜態成員.
class PS4{
companion object{
val FIRMWARE_VERSION:Float = 10.0f
fun boot(){
println("boot...")
}
}
}
跟Java一樣,然後通過類名調用靜態成員
PS4.boot()
PS4.FIRMWARE_VERSION
2.5介面
使用interface來聲明一個介面.與Java的用法並無二致
interface Callback{
fun onSuccess()
fun onFailed()
}
3.泛型
基本使用和Java一致
class Data<T>(private val data:T){
fun get():T{
return data
}
}
val data1 = Data("String")
println(data1.get())
val data2 = Data(16)
println(data2.get())
3.1out和in關鍵字
Kotlin中使用out和in來代替? extends 和? super使用,具體用法跟Java是類似的.
fun main(){
val ps4List = mutableListOf<PS4>()
setGameConsoles(ps4List)
val gameConsoles = getGameConsoles()
for (gameConsole in gameConsoles){
val ps4 = gameConsole as PS4
ps4.play()
}
}
// out ----> ? extends GamesConsole
fun setGameConsoles(gamConsoles:MutableList<out GameConsole>){
}
//in -----> ? super PS4
fun getGameConsoles():MutableList<in PS4>{
val gameConsoles = mutableListOf<GameConsole>()
gameConsoles.add(PS4())
gameConsoles.add(PS4())
return gameConsoles
}
open class GameConsole{
open fun play(){
}
}
class PS4 : GameConsole(){
override fun play() {
println("ps4 play...")
}
}
class NintendoSwitch : GameConsole(){
override fun play() {
println("ns play...")
}
}
4.委托
其實就是代理設計模式.
4.1委托的使用場景
Java中的代理模式實現
//支付介面
public interface IPay {
void pay();
}
//支付寶支付
public class AliPay implements IPay{
private float mMoney;
public AliPay(float money){
mMoney = money;
}
@Override
public void pay() {
Log.d("Alipay","支付寶支付" + mMoney + "元...");
}
}
//微信支付
public class WeChatPay implements IPay{
private float mMoney;
public WeChatPay(float money){
mMoney = money;
}
@Override
public void pay() {
Log.d("WeChatPay","微信支付" + mMoney + "元...");
}
}
//代理對象
public class ProxyPay implements IPay{
private IPay mPay;
public ProxyPay(IPay pay){
mPay = pay;
}
@Override
public void pay() {
mPay.pay();
}
}
Kotlin的代理模式實現
interface IPay{
fun pay()
}
class Alipay(private val money:Float) :IPay{
override fun pay() {
println("支付寶支付$money...")
}
}
class WeChatPay(private val money: Float) :IPay{
override fun pay() {
println("微信支付$money...")
}
}
class PayDelegate(private val play:IPay) :IPay by play{
//什麼都不需要做,系統幫我們完成一系列操作
}
可以看出,Kotlin的委托其實就是簡化了代理模式的實現過程.
4.2屬性委托
將屬性的賦值操作交給其他類來代理.可以通過其他類來統一控制屬性的取值,合法性等等操作.
基本使用
//聲明一個介面,不一定要介面也可以是一個類
interface IPropertyDelegate
class PS4 :IPropertyDelegate{
var version:String by DataDelegate()
}
class NintendoSwitch :IPropertyDelegate{
var version:String by DataDelegate()
}
class DataDelegate {
private var version:String = ""
operator fun setValue(thisRef: IPropertyDelegate, property: KProperty<*>, value: String) {
this.version = value
}
operator fun getValue(thisRef: IPropertyDelegate, property: KProperty<*>): String {
return this.version
}
}
當給PS4或者NintendoSwitch對象的version賦值的時候,就會去到DataDelegate對象的setValue()方法,達到統一賦值的操作,相同的操作不需要在每個類都寫一次
val ps4 = PS4()
ps4.version = "7.55"
println(ps4.version)
val ns = NintendoSwitch()
ns.version = "13.0"
println(ns.version)
5.其他
5.1擴展函數
給某個類添加一個擴展函數,其效果跟成員函數調用一致.一般定義到一個統一的文件中,不需要先定義一個類型,直接寫就好
//給String擴展了一個first方法
fun String.first():Char{
return this[0]
}
println("komine".first())
5.1擴展屬性
給某個類添加一個擴展屬性
val Int.dp:Int
get(){
//簡單模擬一下,開發中不是這麼計算的
return this * 1.5f.toInt()
}
比如給Int類型添加了一個dp的擴展屬性,可以將int值轉換為對應的dp
val width = 39.dp
待更新...