<h3>Kotlin中類和對象初始化</h3>
<ul>
<li>添加open關鍵字代表可以被繼承</li>
<li>Any 是所有類的父類,類似Object,包含 equals() hashCode() toString()方法</li>
<li>constructor 關鍵字代表構造函數, constructor關鍵字可以去掉</li>
</ul>
<pre><code class="java">class User(性格: String, 長相: String, 聲音: String) : Human(性格, 長相, 聲音)
class User2(name: String) : Human2(name)
open class Human constructor(var 性格: String, var 長相: String, var 聲音: String) : Any() {
//對象創建時調用,構造方法的方法體
init {
println("new 了一個${this.javaClass.simpleName}, ta性格:$性格, 長相:$長相, 聲音:$聲音")
}
override fun toString(): String {
return "${性格} 性格"
}
}
open class Human2 {
val name: String
val age: Int
constructor(name: String) : this(name, 0) {
// 調用主構造函數或另一個輔助構造函數
}
? ? constructor(name: String, age: Int = 18) { ?// 輔助構造函數
this.name = name
this.age = age
val user: User = User("溫柔", "甜美", "動人")
? ? ? ? val value: String? = getName()
println(value!!.length)
}
}
</code></pre>
<h3>空類型和智能類型轉換</h3>
<ul>
<li>?.在遇到null時會靜默跳過執行(返回null)</li>
<li>!!在遇到null時會立即拋出異常 (當對可空變量使用!!時,編譯器會假定該變量不為null)</li>
</ul>
<pre><code>fun getName(): String? {// ? 表示返回值可以為空
return "na"//null
}
fun main(args: Array<String>) {
val str: String? = null
// ?.在遇到null時會靜默跳過執行(返回null)
// !!在遇到null時會立即拋出異常 (當對可空變量使用!!時,編譯器會假定該變量不為null)
//val length = str!!.length
// 應盡量避免使用!!,推薦替代方案包括:
// 優先使用val聲明不可變變量
// 對延遲初始化變量使用 lateinit. var用lateinit 延遲初始化,val用lazy
lateinit var q: String
// 使用Elvis操作符?:提供默認值或者return
//Elvis操作符?: 結構:表達式A ?: 表達式B
//當表達式A非null時返回A的值,否則返回表達式B的結果
// 例如:
val length2 = str?.length ?: 0
val name: String = getName() ?: return
println(name.length)
main2(args)
}
</code></pre>
<h3>區間 Range,表示范圍</h3>
<pre><code>//val:用于運行時常量,相當于Java的final字段,支持任何類型且僅初始化一次
//const:用于編譯時常量,相當于Java的public static final字段,可通過類名直接訪問。
//編譯時確定值:const修飾的常量值必須在編譯時確定,不能通過運行時計算或動態賦值。
//類型限制:僅支持基本類型(如int、double、string)和字符串。
//存儲位置:必須聲明在頂層(如包級可見變量)或伴生對象(companion object)中
val range: IntRange = 0..1024 // [0, 1024]
val range_exclusive: IntRange = 0 until 1024 // [0, 1024) = [0, 1023]
val a = (50 in range) // (i in range)判斷i是否在區間中
</code></pre>
<h3>Unit</h3>
在 Kotlin 中,Unit 是一個特殊的類型,類似于 Java 中的 void,但有以下關鍵區別:
<ul>
<li>Unit 是一個實際存在的類型(單例對象),而 void 只是關鍵字</li>
<li>當函數不返回有意義的值時,Kotlin 會隱式返回 Unit</li>
<li>Unit 的實例可以用 Unit 或 () 表示</li>
</ul>
<pre><code>fun printMessage(msg: String): Unit { // 這里的 : Unit 可以省略
println(msg)
// 隱式返回 Unit
}
fun printUsage() {
println("請傳入兩個整型參數,例如 1 2") // (Any?) -> Unit
} // ()->Unit
val sum = { arg1: Int, arg2: Int ->
println("$arg1 + $arg2 = ${arg1 + arg2}")
arg1 + arg2
}
// (Int, Int) -> Int
val printlnHello = {
println(::printUsage is ()-> Unit)
println("Hello")
}
// ()-> Unit
val int2Long = fun(x: Int): Long {
return x.toLong()
}
</code></pre>
<h3>繼承與實現</h3>
<ul>
<li>父類需要 open 才可以被繼承</li>
<li>父類方法、屬性需要 open 才可以被覆寫</li>
<li>接口、接口方法、抽象類默認為 open</li>
<li>覆寫父類(接口)成員需要 override 關鍵字</li>
<li>class D: A(),B,C</li>
<li>注意繼承類時實際上調用了父類構造方法</li>
<li>類只能單繼承,接口可以多實現</li>
</ul>
<h3>繼承之接口代理(基于 by 關鍵字)</h3>
通過 by 將接口實現委托給其他對象,無需手動編寫代理類
<pre><code>class SeniorManager(val driver: Driver, val writer: Writer): Driver by driver, Writer by writer
class CarDriver: Driver {
override fun drive() {
println("開車呢")
}
}
class PPTWriter: Writer {
override fun write() {
println("做PPT呢")
}
}
interface Driver{
fun drive()
}
interface Writer{
fun write()
}
fun main3(args: Array<String>) {
val driver = CarDriver()
val writer = PPTWriter()
val seniorManager = SeniorManager(driver, writer)
seniorManager.drive()
seniorManager.write()
}
</code></pre>
<h3>接口方法沖突</h3>
<ul>
<li>接口方法可以有默認實現</li>
<li>簽名一致且返回值相同的沖突</li>
<li>子類(實現類)必須覆寫沖突方法</li>
<li>super<[父類(接口)名]>.<a href="[參數列表]">方法名</a></li>
</ul>
<pre><code>abstract class A{
open fun x(): Int = 5
}
interface B{
fun x(): Int = 1
}
interface C{
fun x(): Int = 0
}
class D(var y: Int = 0): A(), B, C{
override fun x(): Int {
println("call x(): Int in D")
if(y > 0){
return y
}else if(y < -200){
return super<C>.x()
}else if(y < -100){
return super<B>.x()
}else{
return super<A>.x()
}
}
}
//3 5 1 0?
fun main(args: Array<String>) {
println(D(3).x())
println(D(-10).x())
println(D(-110).x())
println(D(-10000).x())
}
</code></pre>
<h3>可見性對比</h3>
模塊通常指一組共同編譯的 Kotlin 文件(如 Gradle 模塊或 IntelliJ 模塊)
<table>
<thead>
<tr>
<th align="center">kotlin</th>
<th align="center">java</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">private</td>
<td align="center">private</td>
</tr>
<tr>
<td align="center">protected</td>
<td align="center">protected</td>
</tr>
<tr>
<td align="center">-</td>
<td align="center">default(包內可見)</td>
</tr>
<tr>
<td align="center">internal(模塊內可見,模塊化開發的核心修飾符)</td>
<td align="center">-</td>
</tr>
<tr>
<td align="center">public</td>
<td align="center">public</td>
</tr>
</tbody>
</table>
<h3>Object 關鍵字</h3>
object 關鍵字用于實現?單例模式?、?伴生對象?和?對象表達式?(匿名對象),是 Kotlin 特有的簡化設計模式的語法糖。以下是其三大核心用途及示例:
1. 單例模式(對象聲明)?
直接通過 object 聲明線程安全的單例:
<pre><code>object DatabaseManager {
private val connection = "DB_Connection"
fun query(sql: String) = println("Executing: $sql via $connection")
}
// 使用(全局唯一實例)
fun main() {
DatabaseManager.query("SELECT * FROM users") ?// 輸出: Executing: SELECT * FROM users via DB_Connection
}
</code></pre>
特點:
* 只有一個實例的類
* 首次訪問時延遲初始化(線程安全)
* 不能自定義構造函數, 可繼承父類 可實現接口
<ol>
<li>伴生對象
<ul>
<li>每個類可以對應一個伴生對象</li>
<li>伴生對象的成員全局獨一份 , 相當于靜態方法 靜態屬性</li>
</ul></li>
</ol>
<pre><code>class Latitude private constructor(val value: Double){
companion object{
@JvmStatic
fun ofDouble(double: Double): Latitude{
return Latitude(double)
}
? ? ? ? fun ofLatitude(latitude: Latitude): Latitude{
return Latitude(latitude.value)
}
? ? ? ? @JvmField
val TAG: String = "Latitude"
}
}
</code></pre>
<ol>
<li>匿名對象
<ul>
<li>每次調用都會創建新對象</li>
<li>可同時繼承類和實現接口(如 object : ClassA(), InterfaceB</li>
<li>注意:object 與 class 不同,不能實例化(本身就是實例)</li>
</ul></li>
</ol>
<pre><code> ? ?interface ClickListener {
fun onClick()
}
? ? fun setClickListener(listener: ClickListener) {
listener.onClick()
}
? ? fun main() {
var counter = 0
setClickListener(object : ClickListener {
override fun onClick() {
println("Clicked ${++counter} times") ?// 捕獲并修改外部變量
}
})
}
</code></pre>
<h3>擴展成員</h3>
<ul>
<li>為現有類添加方法、屬性</li>
<li>fun X.y(): Z {... }</li>
<li>val x.m 注意擴展屬性不能初始化,類似接口屬性</li>
<li>Java 調用擴展成員類似調用靜態方法</li>
</ul>
<pre><code> ? ?operator fun String.times(int: Int): String{
val stringBuilder = StringBuilder()
for(i in 0 until int){
stringBuilder.append(this)
}
return stringBuilder.toString()
}
? ? val String.a: String
get() = "abc"
? ? var String.b: Int
set(value) {
}
get() = 5
//abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
//5
fun main(args: Array<String>) {
println("abc" * 16)
"abc".b = 5
println("abc".b)
}
</code></pre>
<h3>operator關鍵字</h3>
operator 關鍵字用于?重載運算符?或?約定特定函數?,讓類實例支持類似基礎類型的操作(如 +、[]、in 等)
<h4>運算符重載?</h4>
通過重載預定義的運算符函數,使對象支持算術、比較等操作
<pre><code>data class Point(val x: Int, val y: Int) {
// 重載 "+" 運算符
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
fun main() {
val p1 = Point(1, 2)
val p2 = Point(3, 4)
println(p1 + p2) ?// 輸出: Point(x=4, y=6)
}
</code></pre>
<table>
<thead>
<tr>
<th>運算符</th>
<th>對應函數名</th>
<th>示例表達式</th>
</tr>
</thead>
<tbody>
<tr>
<td>+</td>
<td>plus</td>
<td>a + b</td>
</tr>
<tr>
<td>-</td>
<td>minus</td>
<td>a - b</td>
</tr>
<tr>
<td>[]</td>
<td>get</td>
<td>a[i]</td>
</tr>
<tr>
<td>in</td>
<td>contains</td>
<td>a in b</td>
</tr>
<tr>
<td>==</td>
<td>equals</td>
<td>a==b</td>
</tr>
</tbody>
</table>
<h4>約定函數</h4>
operator 還可用于標記?特定名稱的函數?,實現與語言特性的交互:
<ul>
<li>解構聲明(Destructuring)</li>
</ul>
<pre><code>class User(val name: String, val age: Int) {
operator fun component1() = name ?// 解構為 (name, age)
operator fun component2() = age
}
fun main() {
val user = User("Alice", 25)
val (name, age) = user ?// 解構賦值
println("$name, $age") ?// 輸出: Alice, 25
}
</code></pre>
<ul>
<li>迭代器支持</li>
</ul>
<pre><code>class Counter(val range: IntRange) {
operator fun iterator(): Iterator<Int> = range.iterator()
}
fun main() {
for (i in Counter(1..5)) { ?// 支持 for-in 循環
print("$i ") ?// 輸出: 1 2 3 4 5
}
}
</code></pre>
<h4>注意事項?</h4>
<ul>
<li>不可隨意創造運算符?:只能重載 Kotlin 預定義的運算符47。</li>
<li>一致性要求?:如 plus 不應修改原對象,應返回新實例35。</li>
<li>與擴展函數結合?:可為現有類添加運算符支持</li>
</ul>
<pre><code>operator fun Int.times(str: String) = str.repeat(this)
fun main() {
println(3 * "Hi ") ?// 輸出: Hi Hi Hi?
}
</code></pre>
<h3>屬性代理</h3>
<h4>懶加載 lazy</h4>
<ul>
<li>延遲到首次訪問時執行</li>
<li>線程安全模式?:默認 LazyThreadSafetyMode.SYNCHRONIZED,可選 PUBLICATION(允許多線程初始化)或 NONE(單線程)</li>
</ul>
<pre><code>class Delegates{
val hello by lazy {
"HelloWorld"
}
val hello0 by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
"HelloWorld"
}
? ? val hello2 by X()
? ? var hello3 by X()
}
</code></pre>
<h4>基本用法</h4>
<pre><code>// val 屬性?:只需實現 getValue?
?// var 屬性?:需同時實現 getValue 和 setValue
class X{
private var value: String? = null
? ? operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("getValue: $thisRef -> ${property.name}")
return value?: ""
}
? ? operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String){
println("setValue, $thisRef -> ${property.name} = $value")
this.value = value
}
}
fun main6(args: Array<String>) {
val delegates = Delegates()
println(delegates.hello)
println(delegates.hello2)
println(delegates.hello3)
delegates.hello3 = "value of hello3"
println(delegates.hello3)
println(delegates.hello)
}
//HelloWorld
//getValue: com.spro.globalsearch.Delegates@1593948d -> hello2
//
//getValue: com.spro.globalsearch.Delegates@1593948d -> hello3
//
//setValue, com.spro.globalsearch.Delegates@1593948d -> hello3 = value of hello3
//getValue: com.spro.globalsearch.Delegates@1593948d -> hello3
//value of hello3
//HelloWorld
</code></pre>
<h3>數據類DataClass</h3>
<ul>
<li>自動生成標準方法:
數據類會自動生成 equals()、hashCode()、toString()、copy() 等方法,普通類需手動實現</li>
<li>主構造函數要求:
必須至少有一個參數,且參數需標記為 val 或 va, 普通類無此限制</li>
<li>解構聲明支持
數據類自動支持解構,可直接將屬性拆分為變,普通類則需手動實現 componentN()</li>
<li>不可變性推薦?:
屬性通常聲明為 val(不可變),但支持 var(可變)?</li>
<li>繼承限制?
數據類不能是 abstract、open、sealed 或 inner 類?</li>
<li>無無參構造函數
需顯式定義默認值或輔助構造函數?</li>
</ul>
<h4>應用</h4>
<ul>
<li>數據傳輸對象
如 JSON 解析后的數據實體?</li>
<li>簡化數據操作
利用 copy() 實現不可變對象的修改?
val user2 = user1.copy(age = 26)</li>
</ul>
<pre><code>data class Country(val id: Int, val name: String)
data class Country2(var id: Int, var name: String)
//component 組件
class ComponentX{
operator fun component1(): String{
return "您好,我是"
}
? ? operator fun component2(): Int{
return 1
}
? ? operator fun component3(): Int{
return 1
}
? ? operator fun component4(): Int{
return 0
}
}
fun main7(args: Array<String>) {
//componentN解構聲明
val china = Country(0, "中國")
println(china)// Country(id=0, name=中國)
println("${china.component1()} ?------ ${china.component2()} ")//0 ------ 中國
val (idx, name) = china //解構賦值
println("$idx ------ ?$name")//0 ----- 中國
? ? val componentX = ComponentX()//解構賦值
val (a, b, c, d) = componentX
println("$a $b$c$d")//您好,我是 110
}
</code></pre>
<h3>內部類</h3>
靜態內部類與非靜態內部類的區別:到底是否持有外部類的狀態 (非靜態內部類持有外部類的狀態,可以訪問外部類的屬性)
<pre><code>open class Outter{
val a: Int = 0
class InnerDefaultStatic{
}
//非靜態內部類
inner class Inner{
val a: Int = 5
fun hello(){
println(this@Outter.a)
}
}
}
interface OnClickListener{
fun onClick()
}
class View{
var onClickListener: OnClickListener? = null
}
fun main(args: Array<String>) {
val innerDefaultStatic = Outter.InnerDefaultStatic()
val inner = Outter().Inner()
val view = View()
//匿名內部類看上去沒有名字,但是編譯生成字節碼文件有自己的ID,類似Outter$1.class
view.onClickListener = object : Outter(), OnClickListener{
override fun onClick() {
? ? ? ? }
}
}
</code></pre>
<h3>枚舉類</h3>
<ul>
<li>實例可數的類,注意枚舉也是類</li>
<li>可以修改構造,添加成員</li>
<li>可以提升代碼的表現力,也有一定的性能開銷</li>
<li>實例可數?指枚舉(enum)類型的成員數量固定且不可擴展。</li>
<li>枚舉通過enum class聲明,其所有成員(如VERBOSE、DEBUG等)均為該類的實例對象,且默認構造函數為私有(private constructor()),因此無法被繼承或擴展</li>
</ul>
<pre><code>enum class LogLevel(val id: Int){
VERBOSE(0), DEBUG(1), INFO(2), WARN(3), ERROR(4), ASSERT(5);
? ? fun getTag(): String{
return "$id, $name"
}
? ? override fun toString(): String {
return "$name, $ordinal"
}
}
println(LogLevel.DEBUG.ordinal)
LogLevel.values().map(::println)
println(LogLevel.valueOf("ERROR"))
//VERBOSE, 0
//DEBUG, 1
//INFO, 2
//WARN, 3
//ERROR, 4
//ASSERT, 5
//ERROR, 4
</code></pre>
<h3>密封類</h3>
子類可數
<pre><code>sealed class PlayerCmd{
}
class Play(val url:String,val position:Long =0):PlayerCmd()
class Seek(val position: Long): PlayerCmd()
object Pause:PlayerCmd()
object Resume:PlayerCmd()
object Stop:PlayerCmd()
enum class PlayerState{
IDLE, PAUSE, PLAYING
}
</code></pre>