kotlin笔记

maven建立项目

  1. 建立项目指令
shell 复制代码
mvn archetype:generate "-DarchetypeArtifactId=maven-archetype-quickstart" "-DinteractiveMode=true"
  1. 填写项目信息
shell 复制代码
Define value for property 'groupId': com.wnan [包名]
Define value for property 'artifactId': Demo [项目名]
Define value for property 'version' 1.0-SNAPSHOT: [版本号]
Define value for property 'package' com.wnan: crykt [二级包名]

Demo
    │  pom.xml
    └─src
        ├─main
        │  └─java
        │      └─crykt
        │              App.java
        └─test
            └─java
                └─crykt
                        AppTest.java
  1. 配置pom文件
xml 复制代码
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.release>21</maven.compiler.release>
        <!-- Kotlin 稳定版 2.2.21(Maven仓库可正常下载) -->
        <kotlin.version>2.2.21</kotlin.version>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
    </properties>

依赖:

xml 复制代码
    
    <dependencies>
        <!-- Kotlin 标准库(必须) -->
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
        <!-- Kotlin 反射(可选,你之前加了就保留) -->
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
    </dependencies>
    

插件:

xml 复制代码
    <!-- 🔴 核心:Kotlin Maven 编译插件(你之前完全没加!) -->
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <executions>
                    <execution>
                        <id>compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <sourceDirs>
                          <sourceDir>${project.basedir}/src/main/java</sourceDir>
                            </sourceDirs>
                        </configuration>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                        <configuration>
                            <sourceDirs>
                        <sourceDir>${project.basedir}/src/test/java</sourceDir>
                            </sourceDirs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- 🔴 必须:让 maven-compiler-plugin 配合 Kotlin 插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <phase>none</phase>
                    </execution>
                    <execution>
                        <id>default-testCompile</id>
                        <phase>none</phase>
                    </execution>
                    <execution>
                        <id>java-compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>java-test-compile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

对于vscode需要重建索引: 在 VS Code 按:Ctrl + Shift + P 输入 → Reload Window

创建和运行第一个程序

创建一个名为 hello.kt 文件,代码如下:

kotlin 复制代码
fun main(args: Array<String>) { 
    println("Hello, World!") 
}

使用 Kotlin 编译器编译应用:

shell 复制代码
kotlinc hello.kt -include-runtime -d hello.jar
  • -d: 用来设置编译输出的名称,可以是 class 或 .jar 文件,也可以是目录。
  • -include-runtime : 让 .jar 文件包含 Kotlin 运行库,从而可以直接运行。

包声明

代码文件的开头一般为包的声明:

kotlin 复制代码
package com.wnan.main
import java.util.*

fun test() {}
class Runoob {}

kotlin源文件不需要相匹配的目录和包,源文件可以放在任何文件目录。就相当于一个命名空间

常量与变量

可变变量定义:var 关键字,

kotlin 复制代码
var <标识符> : <类型> = <初始化值>

不可变变量定义:val 关键字,不可以修改指向的引用,引用或值只能被赋值一次,且必须在初始化阶段完成。

kotlin 复制代码
val <标识符> : <类型> = <初始化值>

// 这种情况也不会被允许,会报错
val weight: Double? = null  
weight = weight?.toDouble() ?: 0.0

常量与变量都可以没有初始化值,但是在引用前必须初始化 编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。

函数定义

函数定义使用关键字 fun,参数格式为:参数 : 类型;不用var关键字声明

kotlin 复制代码
fun sum(a: Int, b: Int): Int {   
// Int 参数,返回值 Int
    return a + b
}

表达式作为函数体,返回类型自动推断:

kotlin 复制代码
fun sum(a: Int, b: Int) = a + b
public fun sum(a: Int, b: Int): Int = a + b   
// public 方法则必须明确写出返回类型

无返回值的函数

kotlin 复制代码
fun printSum(a: Int, b: Int): Unit { 
    print(a + b)
}

// 如果是返回 Unit类型,则可以省略(对于public方法也是这样):
public fun printSum(a: Int, b: Int) { 
    print(a + b)
}

可变长参数函数 函数的变长参数可以用 vararg关键字进行标识:

kotlin 复制代码
fun vars(vararg v:Int){
    for(vt in v){
        print(vt)
    }
}

// 测试
fun main(args: Array<String>) {
    vars(1,2,3,4,5)  // 输出12345
}

lambda(匿名函数)表达式使用实例:

kotlin 复制代码
// 测试
fun main(args: Array<String>) {
    val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
    println(sumLambda(1,2))  // 输出 3
}
kotlin 复制代码
   // 简化写法(更常见)
   val sumLambda = { x: Int, y: Int -> x + y }
kotlin 复制代码
// 普通函数写法 
fun sum(x: Int, y: Int): Int = x + y

下面的代码时错误的

kotlin 复制代码
fun feature3(x: Int){
    x=5
    println(x)
}
// Val cannot be reassigned,编译都过不了

基本数据类型

整数类型 Byte,Short,Int,Long 浮点数类型 Float, Double 字符类型 Char 布尔类型 Boolean 数组类型 Kotlin 中数组是不协变的(invariant)。 IntArray: 存储 Int 类型的数组。 DoubleArray: 存储 Double 类型的数组。 Array<T>: 泛型数组,可以存储任意类型。

  1. 类型转换 Kotlin 在比较不同数据类型时会自动进行类型转换。由于不同的表示方式,较小类型并不是较大类型的子类型,较小的类型不能隐式转换为较大的类型。 这意味着在不进行显式转换的情况下我们不能把 Byte 型值赋给一个 Int 变量。
kotlin 复制代码
val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b // 错误

val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b.toInt() // OK

When 表达式

when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。 when 既可以被当做表达式使用也可以被当做语句使用。如果它被当做表达式,符合条件的分支的值就是整个表达式的值,如果当做语句使用, 则忽略个别分支的值。 when 类似其他语言的 switch 操作符。其最简单的形式如下

kotlin 复制代码
when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // 注意这个块
        print("x 不是 1 ,也不是 2")
    }
}

在 when 中,else 同 switch 的 default。如果其他分支都不满足条件将会求值 else 分支。 如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔。 when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:

kotlin 复制代码
when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

标签

Kotlin 的标签(Label)语法是一种用于控制流跳转 的特殊机制,主要用于解决多层嵌套循环或复杂逻辑中的精准跳转问题。它本质上是给代码块(如循环、lambda表达式)起一个名字,然后通过 breakcontinuereturn 精准指定跳转目标。在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@、fooBar@都是有效的标签。 要为一个表达式加标签,我们只要在其前加标签即可。

kotlin 复制代码
package org.wnan  
  
fun main() {  
    println("=== 场景1:使用标签跳出多层循环 ===")  
    outerLoop@ for (i in 1..3) {  
        innerLoop@ for (j in 1..3) {  
            println("i=$i, j=$j")  
            if (i == 2 && j == 2) {  
                println("触发 break@outerLoop - 跳出外层循环")  
                break@outerLoop // 直接跳出标签为 outerLoop 的循环  
//                break  
            }  
        }  
    }  
    println("外层循环已终止\n")  
  
    println("=== 场景2:使用标签继续外层循环 ===")  
    outer@ for (i in 1..3) {  
        inner@ for (j in 1..3) {  
            if (j == 2) {  
                println("i=$i, j=$j → 触发 continue@outer")  
                continue@outer // 跳过外层循环的当前迭代  
            }  
            println("i=$i, j=$j")  
        }  
    }  
    println("外层循环结束\n")  
  
    println("=== 场景3:在lambda中使用标签返回 ===")  
    val numbers = listOf(1, 2, 3, 4, 5)  
  
    // 使用标签从lambda返回  
    numbers.forEach lit@ { num ->  
        if (num == 3) {  
            println("遇到3,从lambda返回")  
            return@lit // 仅从当前lambda返回,继续执行forEach后的代码  
        }  
        println("处理数字: $num")  
    }  
    
    println("lambda处理完成\n")  
  
    println("=== 场景4:嵌套lambda中的标签返回 ===")  
    run outer@ {  
        println("进入外层run块")  
        listOf("A", "B", "C").forEach inner@ { item ->  
            if (item == "B") {  
                println("遇到B,从外层run块返回")  
                return@outer // 直接从标签为outer的run块返回  
            }  
            println("处理项目: $item")  
        }  
        println("这行不会执行") // 因为遇到B时已经从outer返回  
    }  
    println("外层run块已终止")  
}
plain 复制代码
=== 场景1:使用标签跳出多层循环 ===
i=1, j=1
i=1, j=2
i=1, j=3
i=2, j=1
i=2, j=2
触发 break@outerLoop - 跳出外层循环
外层循环已终止

=== 场景2:使用标签继续外层循环 ===
i=1, j=1
i=1, j=2 → 触发 continue@outer
i=2, j=1
i=2, j=2 → 触发 continue@outer
i=3, j=1
i=3, j=2 → 触发 continue@outer
外层循环结束

=== 场景3:在lambda中使用标签返回 ===
处理数字: 1
处理数字: 2
遇到3,从lambda返回
处理数字: 4
处理数字: 5
lambda处理完成

=== 场景4:嵌套lambda中的标签返回 ===
进入外层run块
处理项目: A
遇到B,从外层run块返回
外层run块已终止

空类型

Kotlin 中,类型系统默认所有类型都是非空 的。这意味着一个普通的 String 类型的变量,你无法给它赋值为 null

kotlin 复制代码
fun mian(){
	var name: String = "Kotlin"
	// name = null // 编译错误!Null 不能赋值给非空类型
}

如果你需要一个变量能够存储 null,必须显式地在类型后面加上 ? 来声明为可空类型

kotlin 复制代码
fun main() {
    var name: String? = "kotlin"
    name = null
}

Elvis 操作符 ?:通常与安全调用操作符配合使用,用于在表达式结果为 null 时提供一个默认值

kotlin 复制代码
fun main() {
    var name: String? = "kotlin"
    name = null
    var length:Int = name?.length ?: 0
    println(length)
}

智能转换

当你使用 if 语句对一个可空变量进行了非空检查后,在该 if 代码块内部,编译器会自动将该变量视为非空类型,你可以直接使用它,无需任何额外操作。

kotlin 复制代码
fun printName(name: String?) {
    if (name != null) {
        // 在这里,name 已经被智能转换为非空的 String 类型
        println("Name length: ${name.length}")
    } else {
        println("Name is null")
    }
}

let函数

let 是一个作用域函数,与 ?. 结合使用时,可以在变量不为空时执行一段代码块。

kotlin 复制代码
fun main() {
    val v1: Int = 1
    val v2: Int? = null
    val v3: Int = 3
    var ls: List<Int?> = listOf(v1, v2, v3)
    ls.forEach { p -> p?.let { println(p) } }
}
//输出
// 1
// 3

非空断言操作符 !!

这是一个"危险"的操作符。它的作用是告诉编译器:"我保证这个值不为空,如果为空,就抛出异常!"。不建议使用

kotlin 复制代码
val name: String? = null
// 下面这行代码会抛出 KotlinNullPointerException
val length = name!!.length

延迟初始化

第一种:lateinit 关键字

  • 用在 类的成员变量顶层属性局部变量
  • 只能是 引用类型,不能是基本类型(Int/Long/Boolean 不行)
  • 只能用 var,不能用 val
  • 后续手动提前赋值,用之前必须赋值,否则崩溃
  • 不是懒加载,只是允许先不初始化,后面再赋值
kotlin 复制代码
class Person {
    // 延迟初始化,先不赋值
    lateinit var name: String

    fun initName() {
        // 后面再赋值
        name = "张三"
    }

    fun show() {
        // 赋值后才能使用
        println(name)
    }
    
    fun check() {
        if (::name.isInitialized) {
            println("已初始化:$name")
        } else {
            println("还没初始化")
        }
    }
}

fun main() {
    val p = Person()
    p.initName()
    p.show()
}

第二种:lazy 懒加载(最常用)

  • 只能用 val 只读变量
  • 第一次使用时才初始化,之后永远用这个值
  • 线程安全、自带缓存
  • 不需要手动管理赋值,不用怕空指针
  • 纯 Kotlin 开发、后端、普通类都常用
kotlin 复制代码
fun main() {
    val info: String by lazy {
        println("执行初始化逻辑")
        "我是懒加载字符串"
    }

    println("开始执行 main")
    // 第一次访问,才会执行 lambda 初始化
    println(info)
    // 第二次访问,直接拿缓存,不再执行 lambda
    println(info)
}

/*
输出:
开始执行 main
执行初始化逻辑
我是懒加载字符串
我是懒加载字符串
*/

协程

#协程 导入依赖

kotlin 复制代码
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
相关推荐
唐青枫11 小时前
Kotlin let 详解:空安全、链式转换与实战示例
kotlin
alexhilton19 小时前
车载系统中的可扩展UI:从UI嵌入到系统窗口编排
android·kotlin·android jetpack
日光明媚1 天前
一步生成视频!One-Forcing:DMD + 零成本 GAN,训练 200 步超越多步 SOTA
android·开发语言·kotlin
plainGeekDev1 天前
Android运行时面试题:ART和JVM的区别都搞不清,别写精通了
jvm·面试·kotlin
Refrain_zc3 天前
Android Kotlin + MVVM:基于 LiveData 的段落列表音频播放与 AB 复读实现
kotlin
赏金术士3 天前
企业级 Jetpack Compose 项目(入门版)最佳结构
android·kotlin·compose
我是唐青枫3 天前
Kotlin Lambda 表达式详解:从基础语法到实战封装
开发语言·kotlin
Kapaseker3 天前
Kotlin 的扩展没有你看上去的那么简单
android·kotlin
黄林晴3 天前
告别 KMP 选型地狱!klibs.io 上线,全平台库一键筛选太省心
android·kotlin
吕氏春秋i3 天前
android kotlin Compose 蓝牙库推荐
android·gitee·kotlin