變量
變量的聲明
Kotlin
使用var,val
來聲明變量,注意:Kotlin
不再需要;
來結尾
var
?可變變量,對應java
的非final
變量
var b = 1
val
不可變變量,對應java
的final
變量
val a = 1
兩種變量并未聲明類型,這是因為Kotlin
存在類型推導機制,上述的a,b
會默認為Int
。假設想聲明具體類型,則需下面的方式。
var c: Int = 1
基本類型
Kotlin
不再存在基本類型,將全部使用對象類型
Java基本類型 | Kotlin對象類型 | 對象類型說明 |
---|---|---|
int | Int | 整型 |
long | Long | 長整型 |
short | Short | 短整型 |
float | Float | 單精度浮點型 |
double | Double | 雙精度浮點型 |
boolean | Boolean | 布爾型 |
char | Char | 字符型 |
byte | Byte | 字節型 |
函數
函數的聲明
無參無返回值
fun test() {
}
有參有返回值
參數的類型需要寫在形參名后面中間使用:連接多個參數使用,分割",“返回值使用”:"拼接
fun add(a: Int, b: Int): Int {return a + b
}
聲明技巧
當函數體只有一行代碼時可直接使用下面方式聲明方法
fun add (a: Int, b: Int): Int = a + b
Kotlin
存在類型推導,返回值類型也可省略
fun add (a: Int, b: Int) = a + b
函數的調用
fun main() {test()println(add(1, 2))
}//運行結果
//test
//3
if語句
Kotlin
中的選擇控制有兩種方式。if
和when
if
與Java
的if
區別不大,實現一個返回最大值的函數
fun max(a: Int, b: Int): Int {if (a > b) return aelse return b
}
Kotli
n的if
可以包含返回值,if
語句的最后一行會作為返回值返回
fun max(a: Int, b: Int): Int {return if (a > b) a else b
}
上述我們說過一行代碼可省略返回值
fun max(a: Int, b: Int) = if (a > b) a else b
when語句
實現一個查詢成績的函數,用戶傳入名字,返回成績級別
if實現
Kotlin
的if
語句必須要有else
,不然會報錯
fun getScore(name: String) = if (name == "Tom") "不及格"
else if (name == "Jim") "及格"
else if (name == "Pony") "良好"
else if (name == "Tony") "優秀"
else "名字非法"
Kotlin
中==
等價于Java
的equals
比較的時是對象里的內容,?===
?等價于Java
的==
,比較的為對象的引用。
when實現(相當于swich)
也必須實現else
,否則報錯
fun getScore(name: String) = when(name) {"Tom" -> "不及格""Jim" -> "及格""Pony" -> "良好""Tony" -> "優秀"else -> "名字非法"
}
循環語句
Kotlin
有兩種循環方式,while和for-in,while
與java
中的while
沒有區別,for-in是對Java for-each
的加強,Kotlin
舍棄了for-i
的寫法
while
不再贅述,在學習for-in
之前需要明確一個概念-區間
val range = 0..10 //區間代表[0,10]
for-in
需借助區間來使用
fun main() {val range = 0..10for (i in range) { //也可直接for (i in 0..10)println(i)}//輸出結果為 從0打印到10
}
0..10
?代表雙閉區間,如果想使用左閉右開呢,需要借助until
關鍵字
fun main() {for (i in 0 until 10) {println(i)}//輸出結果為 從0打印到9
}
上述實現是逐步進行相當于i++
,Kotlin
也支持跳步
fun main() {for (i in 0 until 10 step 2) {println(i)}//輸出結果為0,2,4,6,8
}
for-in
不僅可對區間進行遍歷,還可對集合進行遍歷,后續在集合處進行展示。
類和對象
類的創建和對象的初始化
創建Person
類,并聲明name
,age
,創建printInfo
方法
class Person {var name = ""var age = 0fun printInfo() {println(name +"'s age is " + age)}
}
在main
方法中聲明一個Person
對象并調用printInfo
方法
fun main() {val person = Person()person.name = "zjm"person.age = 20person.printInfo()
}
//結果如下zjm's age is 20
繼承
聲明Student
類繼承Person
,Kotlin
中繼承使用**:**,后接父類的構造,為什么需要構造后續講解
class Student : Person(){ //此時Person報錯var number = ""var grade = 0fun study() {println(name + "is studying")}
}
Person
類為final
不可被繼承,因此需借助open關鍵字
只需在Person
類前加上open
open class Person {...
}
構造
構造分為主構造和此構造
主構造
主構造直接寫在類后面
修改Student
類
class Student(val number: String, val grade: Int) : Person(){...
}
因之前Person
還有name
和age
,下面修改Person
類的主構造
open class Person(val name: String, val age: Int) {...
}
此時Student
報錯,因為繼承Person
時,后邊使用的是Person()
無參構造,上面我們修改了Person
的構造,則不存在無參構造了。
再修改Student
class Student(name: String, age: Int, val number: String, val grade: Int) : Person(name, age){...
}
此時不在報錯,聲明方式如下
val student = Student("zjm", 20, "1234", 90)
在構造時需要進行特殊處理怎么辦,Kotlin提供了init結構體,主構造的邏輯可在init
中處理
open class Person(val name: String, val age: Int) {init {println("name is" + name)println("age is" + age)}
}
上述修改都為主構造,那如果類想有多個構造怎么辦,此時需借助次構造
次構造
此時實現Student
的另外兩個構造
三個參數的構造,name
,age
,number
,grade
不傳參默認為``0
無參構造,字符串默認為"",int默認為0
class Student(name: String, age: Int, val number: String, val grade: Int) : Person(name, age){constructor(name: String, age: Int, number: String) : this(name, age, number, 0) {}constructor() : this("", 0, "", 0) {}...
}
創建如下:
fun main() {val student1 = Student("zjm", 20, "123", 90)val student2 = Student("zjm", 20, "123")val student3 = Student()
}
無主構造
若類不使用主構造,則后續繼承類也不需要使用構造即可去掉繼承類的()
,次構造可以調用父類構造super
進行初始化,但是次構造的參數在其他地方無法引用
class Student : Person {constructor(name: String, age: Int, number: String) : super(name, age) {}fun study() {//name,age可使用println(name + "is studying")//使用number則會報錯,若number是主構造的參數則可引用//println(number) 報紅}
}
接口
接口的定義
和Java
中的接口定義類似
interface Study {fun study()fun readBooks()fun doHomework()
}
接口的繼承
繼承接口只需在后用","
拼接,需實現Study
聲明的全部函數
class Student(name: String, age: Int, val number: String, val grade: Int) : Person(name, age), Study{...override fun study() {TODO("Not yet implemented")}override fun readBooks() {TODO("Not yet implemented")}override fun doHomework() {TODO("Not yet implemented")}
}
Kotlin
支持接口方法的默認實現,JDK1.8
以后也支持此功能,方法有默認實現則繼承類無需必須實現此方法
interface Study {fun study() {println("study")}fun readBooks()fun doHomework()
}
權限修飾符
Java
和Kotlin
的不同如下表所示:
修飾符 | Java | Kotlin |
---|---|---|
public | 所有類可見 | 所有類可見(默認) |
private | 當前類可見 | 當前類可見 |
protected | 當前類,子類,同包下類可見 | 當前類,子類可見 |
default | 同包下類可見(默認) | 無 |
internal | 無 | 同模塊下的類可見 |
Kotlin
引入internal
,摒棄了default
使用:
類上
public open class Person(val name: String, val age: Int){...}
?變量上
private val value = 1
方法上
private fun test() {}
數據類和單例類
數據類
數據類則只處理數據相關,與Java Bean
類似,通常需要實現其get
,set
,hashCode
,equals
,toString
等方法
下面實現UserBean
,包含id
,name
,pwd
屬性
Java
編寫入如下:
public class UserBean {private String id;private String name;private String pwd;public UserBean() {}public UserBean(String id, String name, String pwd) {this.id = id;this.name = name;this.pwd = pwd;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;UserBean userBean = (UserBean) o;return Objects.equals(id, userBean.id) && Objects.equals(name, userBean.name) && Objects.equals(pwd, userBean.pwd);}@Overridepublic int hashCode() {return Objects.hash(id, name, pwd);}@Overridepublic String toString() {return "UserBean{" +"id='" + id + '\'' +", name='" + name + '\'' +", pwd='" + pwd + '\'' +'}';}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}
}
Kotlin
編寫此類將變得非常簡單
一行代碼即可搞定,Kotlin會自動實現上述方法。
data class UserBean(val id: String, val name: String, val pwd: String)
若無data
關鍵字,上述方法(hashCode
,equals
,toString
)無法正常運行,去掉data
查看Kotlin
對應的java
文件:
public final class UserBean {@NotNullprivate final String id;@NotNullprivate final String name;@NotNullprivate final String pwd;@NotNullpublic final String getId() {return this.id;}@NotNullpublic final String getName() {return this.name;}@NotNullpublic final String getPwd() {return this.pwd;}public UserBean(@NotNull String id, @NotNull String name, @NotNull String pwd) {Intrinsics.checkNotNullParameter(id, "id");Intrinsics.checkNotNullParameter(name, "name");Intrinsics.checkNotNullParameter(pwd, "pwd");super();this.id = id;this.name = name;this.pwd = pwd;}
}
發現上面代碼既無hashCode
,equals
,toString
也無set
加上data
且把變量改為var
,對應的java
文件如下:
public final class UserBean {@NotNullprivate String id;@NotNullprivate String name;@NotNullprivate String pwd;@NotNullpublic final String getId() {return this.id;}public final void setId(@NotNull String var1) {Intrinsics.checkNotNullParameter(var1, "<set-?>");this.id = var1;}@NotNullpublic final String getName() {return this.name;}public final void setName(@NotNull String var1) {Intrinsics.checkNotNullParameter(var1, "<set-?>");this.name = var1;}@NotNullpublic final String getPwd() {return this.pwd;}public final void setPwd(@NotNull String var1) {Intrinsics.checkNotNullParameter(var1, "<set-?>");this.pwd = var1;}public UserBean(@NotNull String id, @NotNull String name, @NotNull String pwd) {Intrinsics.checkNotNullParameter(id, "id");Intrinsics.checkNotNullParameter(name, "name");Intrinsics.checkNotNullParameter(pwd, "pwd");super();this.id = id;this.name = name;this.pwd = pwd;}@NotNullpublic final String component1() {return this.id;}@NotNullpublic final String component2() {return this.name;}@NotNullpublic final String component3() {return this.pwd;}@NotNullpublic final UserBean copy(@NotNull String id, @NotNull String name, @NotNull String pwd) {Intrinsics.checkNotNullParameter(id, "id");Intrinsics.checkNotNullParameter(name, "name");Intrinsics.checkNotNullParameter(pwd, "pwd");return new UserBean(id, name, pwd);}// $FF: synthetic methodpublic static UserBean copy$default(UserBean var0, String var1, String var2, String var3, int var4, Object var5) {if ((var4 & 1) != 0) {var1 = var0.id;}if ((var4 & 2) != 0) {var2 = var0.name;}if ((var4 & 4) != 0) {var3 = var0.pwd;}return var0.copy(var1, var2, var3);}@NotNullpublic String toString() {return "UserBean(id=" + this.id + ", name=" + this.name + ", pwd=" + this.pwd + ")";}public int hashCode() {String var10000 = this.id;int var1 = (var10000 != null ? var10000.hashCode() : 0) * 31;String var10001 = this.name;var1 = (var1 + (var10001 != null ? var10001.hashCode() : 0)) * 31;var10001 = this.pwd;return var1 + (var10001 != null ? var10001.hashCode() : 0);}public boolean equals(@Nullable Object var1) {if (this != var1) {if (var1 instanceof UserBean) {UserBean var2 = (UserBean)var1;if (Intrinsics.areEqual(this.id, var2.id) && Intrinsics.areEqual(this.name, var2.name) && Intrinsics.areEqual(this.pwd, var2.pwd)) {return true;}}return false;} else {return true;}}
}
此時則和手動編寫的java bean
功能一樣了,所有方法都可正常運行
單例類
目前Java
使用最廣的單例模式的實現如下:
public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}public void test() {...}
}
在Kotlin
中創建單例類需選擇Object
生成代碼如下
object Singleton {fun test() {...}
}
其對應的java
文件如下,和上述使用最多的java
單例實現類似
public final class Singleton {@NotNullpublic static final Singleton INSTANCE;public final void test() {}private Singleton() {}static {Singleton var0 = new Singleton();INSTANCE = var0;}
}
?使用如下:
fun main() {Singleton.test() //對應的java代碼為Singleton.INSTANCE.test();
}
許多高級語言都支持Lambda
,java
在jdk1.8
以后才支持Lamda
語法,Lamda
是Kotlin
的靈魂所在,此小節對Lambda
的基礎進行學習,并借助集合練習。
集合的創建和遍歷
List
fun main() {//常規創建val list = ArrayList<Int>()list.add(1)list.add(2)list.add(3)//listOf不可變,后續不可添加刪除,只能查val list1 = listOf<Int>(1, 2, 3 ,4 ,5)list1.add(6)//報錯//mutableListOf,后續可添加刪除val list2 = mutableListOf<Int>(1, 2, 3 ,4 ,5)list2.add(6)//循環for (value in list2) {println(value)}
}
Set
set
用法與List
類似,只是把listOf
替換為mapOf
Map
fun main() {val map = HashMap<String, String>()map.put("1", "zjm")map.put("2", "ljn")//Kotlin中map支持類似下標的賦值和訪問map["3"] = "lsb"map["4"] = "lyx"println(map["2"])println(map.get("1"))//不可變val map1 = mapOf<String, String>("1" to "zjm", "2" to "ljn")map1["3"] = "lsb" //報錯//可變val map2 = mutableMapOf<String, String>("1" to "zjm", "2" to "ljn")map2["3"] = "lsb"for ((key, value) in map) {println(key + " " + value)}}
Lambda
Lambda的使用
方法在傳遞參數時都是普通變量,而Lambda可以傳遞一段代碼
Lambda表達式的語法結構
{參數名1: 參數類型, 參數名2:參數類型 -> 函數體}
Kotlin的list提供了maxByOrNull函數,返回當前list中xx最大的元素,XX是我們定義的條件,可能為長度,可能是別的,我們拿長度舉例。
若不使用maxBy,實現如下
fun main() {val list = listOf<String>("a", "aba", "aabb", "a")var maxStr = ""for (str in list) {if (str.length > maxStr.length) {maxStr = str;}}println(maxStr)
}
maxByOrNull
是一個普通方法,需要一個Lambda
參數,下面結合Lambda
使用maxByOrNull
fun main() {val list = listOf<String>("a", "aba", "aabb", "a")var lambda = {str: String -> str.length}var maxStr = list.maxByOrNull(lambda)println(maxStr)
}
直接當成參數也可傳遞
var maxStr = list.maxByOrNull({str: String -> str.length})
若Lambda
為方法的最后一個參數,則可將{}
提到外面
var maxStr = list.maxByOrNull() {str: String -> str.length}
若有且僅有一個參數且是Lambda
,則可去掉()
var maxStr = list.maxByOrNull {str: String -> str.length}
Kotlin
擁有出色的類型推導機制,Lambda
參數過多時可省略參數類型
var maxStr = list.maxByOrNull {str -> str.length}
若Lambda
只有一個參數,則可用it
替代參數名
var maxStr = list.maxByOrNull {it.length}
集合還有許多此類函數
創建list,后續操作都由此list
轉換
val list = listOf<String>("a", "aba", "aabb", "a")
map
?映射,返回新集合,將集合中的元素映射成另一個值
val newList = list.map { it.toUpperCase() }//將集合中的元素都準換成大寫
filter
過濾,返回新集合,將集合中的元素進行篩選
val newList = list.filter { it.length > 3 }//篩選出長度大于3的元素
any
返回Boolean
,集合中是否存在元素滿足Lambda
的條件,有則返回true
,無則false
val isAny = list.any {it.length > 10} //返回false
all
返回Boolean
,集合中元素是否全部滿足滿足Lambda
的條件,有則返回true
,無則false
val isAll = list.all {it.length > 0} //返回true
Lambda
的簡單使用到這就結束了
Java函數式API的使用
Kotlin
調用Java
方法,若該方法接收一個Java
單抽象方法接口參數,則可使用函數式API
。Java
單抽象方法接口指的是接口只聲明一個方法,若有多個方法則無法使用函數式API
。
Java
單抽象方法接口例如Runnable
public interface Runnable {void run();
}
在Java
中啟動一個線程如下:
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("test");}
}).start();
Kotlin
啟動線程如下:
Kotlin
摒棄了new
,若想聲明匿名內部類必須使用object
Thread(object : Runnable {override fun run() {println("test")}
}).start()
因Runnable
是Java
單抽象方法接口,可對代碼進行簡化
Thread(Runnable {println("test")}).start()
Runnable
接口只用一個方法,使用Lambda
也不會有歧義,Kotlin
知道此Lambda
一定實現的為run
函數,借用Lambda
進一步簡化:
Thread({println("test")
}).start()
又因Thread
只需一個參數Runnable
參數,則可省略()
Thread {println("test")
}.start()
與上類似的,click
也使用上述方法
button.setOnClickListener { println("test") }
這種方式可極大縮減代碼量
空指針檢查機制
國外統計程序出現最多的異常為空指針異常,Kotlin
存在編譯時檢查系統幫助我們發現空指針異常。
查看下面Java
代碼
public void doStudy(Study study) {study.doHomework();study.readBooks();
}
上述代碼時存在空指針風險的,傳入null,則程序崩潰,對其進行改進
public void doStudy(Study study) {if (study != null) {study.doHomework();study.readBooks();}
}
對于Kotlin
來講任何參數和變量不能為空
fun study(study: Study) {study.doHomework()study.readBooks()
}fun main() {study(null) //報錯study(Student()) //正確
}
Kotlin
把空指針異常的檢查提前到了編譯期,若空指針則編譯期就會崩潰,避免在運行期出現問題
若我們有特殊的需求可能需要傳遞null參數,參數則按照下面聲明
fun study(study: Study?) {study.doHomework() //報錯study.readBooks() //報錯
}
?
的意思則是當前參數可為空,如果可為空的話,則此對象調用的方法必須要保證對象不為空,上面代碼沒有保證,則報錯,修改如下
fun study(study: Study?) {if (study != null) {study.doHomework()study.readBooks()}
}
也可借助判空輔助工具
判空輔助工具
?.
其含義是?
前面對象不為空才執行.
后面的方法
fun study(study: Study?) {study?.doHomework()study?.readBooks()
}
?:
其含義是?
前不為空則返回問號前的值,為空則返回:
后的值
比如
val c = if (a !=null ) {a
} else {b
}
借助?:
則可簡化為
val c = a ?: b
再比如
fun getTextLength(text: String?): Int {if (text != null) {return text.length}return 0
}
借助?:
?則可簡化為
fun getTextLength(text: String?) = text?.length ?: 0
!!
有些時候我們想要強行通過編譯,就需要依靠!!,這時就是程序員來保證安全
fun study(study: Study?) {//假設此時為空拋出異常,則和java一樣study!!.doHomework()study!!.readBooks()
}
let函數
let
不是關鍵字,而是一個函數,提供了函數式API
的編程接口,會將調用者作為參數傳遞到Lambda
表達式,調用之后會立馬執行Lambda
表達式的邏輯
obj.let { it -> //it就是obj//編寫操作
}
比如上面函數
fun study(study: Study?) {study.doHomework() //報錯study.readBooks() //報錯
}
借助let
則可改為
fun study(study: Study?) {//此時靠?.則保證了study肯定不為空,才會執行let函數study?.let {//it為studyit.doHomework()it.readBooks()}
}
全局判空注意事項
//全局變量
var study: Study? = null
fun study() {//報錯if (study != null) {study.readBooks()study.doHomework()}
}
因全局變量隨時有可能被其他線程修改,即使判空處理也不能保證其沒有空指針風險,而let
則可規避上述問題
var study: Study? = null
fun study() {study?.let {it.doHomework()it.readBooks()}
}
內嵌表達式?$
之前我們拼接字符串都是下面這樣
var name = "zjm"
var age = 20
println("My name is " + name + ". I am " + age + ".")
//打印結果
//My name is zjm. I am 20.
現在靠著Kotlin
提供的內嵌表達式則不需要拼接,只需要下面這樣則可實現
var name = "zjm"
var age = 20
println("My name is $name. I am $age." )
//打印結果
//My name is zjm. I am 20.
內嵌表達式復雜操作
${程序員想要的操作}
var name = "zjm"
var age = 20
println("My name is ${if (1 < 2) "zjm" else "ljn"}. I am $age." )
//打印結果
//My name is zjm. I am 20.
函數的參數默認值
Kotlin
支持函數存在默認值,使用如下
fun main() {myPrint(1)myPrint(1, "lalala")
}fun myPrint(value: Int, str: String = "hello") {println("num is $value, str is $str")
}//結果如下
//num is 1, str is hello
//num is 1, str is lalala
若value
想為默認值,則會報錯,因為在使用時傳入的第一個參數他認為是int
的,傳入字符串會類型不匹配
fun main() {myPrint("zjm")//報錯
}fun myPrint(value: Int = 100, str: String) {println("num is $value, str is $str")
}
Kotlin
提供了一種鍵值對傳參來解決上述問題
fun main() {myPrint(str = "zjm") //正確調用
}fun myPrint(value: Int = 100, str: String) {println("num is $value, str is $str")
}
回顧之前的主次構造,Student
如下
class Student(name: String, age: Int, val number: String, val grade: Int) : Person(name, age){constructor(name: String, age: Int, number: String) : this(name, age, number, 0) {}...
}
上述的此構造借助參數默認值技巧是可以不寫的,將第四個參數默認值為0 即可
class Student(name: String, age: Int, val number: String, val grade: Int = 0) : Person(name, age){...
}
by lazy
Kotlin 的?by lazy
?是一種??延遲初始化屬性??的委托機制,主要用于優化資源加載、減少啟動開銷,并簡化代碼邏輯
1. ??基本特性與工作原理??
- ??延遲初始化??:
by lazy
?定義的屬性僅在首次訪問時執行初始化邏輯,后續訪問直接返回緩存值。 - ??只讀屬性??:僅適用于?
val
(不可變變量),初始化后值不可修改。 - ??線程安全??:默認使用?
LazyThreadSafetyMode.SYNCHRONIZED
?模式,通過雙重檢查鎖(double-checked locking)確保多線程安全
// 默認線程安全模式
val database: Database by lazy {Database.connect("jdbc:mysql://localhost:3306/mydb")
}
???Android 中的典型應用場景??
- ??ViewModel 初始化??:推遲創建直到首次訪問,避免?
Activity
/Fragment
?構造時的額外開銷
class MainActivity : AppCompatActivity() {private val viewModel by lazy { ViewModelProvider(this).get(MainViewModel::class.java) }
}
- 高開銷資源??:如數據庫連接、文件讀取或網絡客戶端。
- ??按需加載視圖??:僅在需要時初始化復雜 UI 組件(如自定義控件)。
lateinit
Kotlin 的?lateinit
?是一種??延遲初始化非空可變屬性??的機制,主要用于解決對象在聲明時無法立即賦值但后續保證會被初始化的問題
可變屬性??
- 僅適用于?
var
?變量,不可用于?val
(常量)。 - ??非空類型限制??:只能用于對象類型(如?
String
、View
),??不支持原始數據類型??(如?Int
、Boolean
)
Android 中的典型應用場景??
??視圖綁定與控件初始化??
- 在?
Activity
/Fragment
?的?onCreate()
?中初始化?View
,避免聲明時立即加載資源
- 在?
class MainActivity : AppCompatActivity() {lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater) // 手動初始化setContentView(binding.root)}
}
2.ViewModel 屬性賦值??
- 在?
ViewModel
?中聲明非空屬性,并在?Activity
?中通過?ViewModelProvider
?賦值
class MyViewModel : ViewModel() {lateinit var userData: String // 稍后由 Activity 賦值
}
by lazy與?lateinit
?的對比??
by lazy
?和?lateinit
?均用于延遲初始化,但適用場景不同:
??特性?? | ??by lazy ?? | ??lateinit ?? |
---|---|---|
??適用變量類型?? | val (只讀) | var (可變) |
??初始化時機?? | 首次訪問時自動初始化 | 需手動顯式初始化 |
??線程安全?? | 默認支持(可配置模式) | 無內置線程安全 |
??支持數據類型?? | 所有類型(含基本類型) | 僅非空對象類型(不含?Int ?等) |
??典型場景?? | 單例、高開銷資源、只讀配置 | Android 視圖綁定、依賴注入 |