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")
相关推荐
黄林晴2 小时前
重磅更新!Kotlin协程1.11.0 发布,Flow/StateFlow 新 API 全面升级
android·kotlin
帅次3 小时前
Navigation Compose:NavHost、NavController 与参数
android·kotlin·gradle·android jetpack·compose
UXbot1 天前
AI画原型工具如何帮非设计师快速生成UI界面
前端·vue.js·ui·kotlin·swift·原型模式·web app
赏金术士1 天前
JetPack Compose 弹窗、菜单、交互组件(五)
android·kotlin·交互·android jetpack·compose
小书房1 天前
Kotlin的协程
kotlin·高并发·协程·异步·虚拟线程·coroutinescope
小书房1 天前
Kotlin协程的运行原理
android·开发语言·kotlin·协程
赏金术士1 天前
JetPack Compose 基础核心模块(一)
android·kotlin·android jetpack·compose
alexhilton1 天前
如何用Perfetto来对启动优化去伪存真
android·kotlin·android jetpack
赏金术士2 天前
Kotlin 从入门到进阶 之函数模块(核心基础)(二)
android·开发语言·kotlin