Java 转 Kotlin 对照开发指南
一、基本类型对照表
|-----------------|-------------------|------------|----------------------|--------------------------|
| Java 类型 | Kotlin 类型 | 说明 | Java 示例 | Kotlin 示例 |
| int | Int | 整型 | int num = 10; | val num: Int = 10 |
| long | Long | 长整型 | long bigNum = 100L; | val bigNum: Long = 100L |
| double | Double | 双精度浮点 | double price = 9.99; | val price: Double = 9.99 |
| float | Float | 单精度浮点 | float pi = 3.14f; | val pi: Float = 3.14f |
| boolean | Boolean | 布尔型 | boolean flag = true; | val flag: Boolean = true |
| char | Char | 字符 | char c = 'A'; | val c: Char = 'A' |
| String | String | 字符串 | String name = "Tom"; | val name: String = "Tom" |
| Integer | Int? | 可空整型 | Integer num = null; | val num: Int? = null |
二、变量声明对照
|--------------------------------------------|--------------------------------------|------------|
| Java 写法 | Kotlin 写法 | 说明 |
| int x = 5; | val x: Int = 5 | 不可变变量 |
| String name = "John"; | var name: String = "John" | 可变变量 |
| final int MAX = 100; | const val MAX = 100 | 编译时常量 |
| List<String> list = new ArrayList<>(); | val list = mutableListOf<String>() | 集合初始化 |
三、控制流对照
1. 条件语句
java
// Java
if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
Kotlin
// Kotlin
if (score >= 60) {
println("及格")
} else {
println("不及格")
}
// 或作为表达式
val result = if (score >= 60) "及格" else "不及格"
2. 循环语句
java
// Java for循环
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
// Kotlin for循环
for (i in 0..9) {
println(i)
}
// 或
for (i in 0 until 10) {
println(i)
}
四、函数/方法对照
|--------------------------------------------------------------------------------|----------------------------------------------------------------------------|------------|
| Java 方法 | Kotlin 函数 | 说明 |
| java<br>public int add(int a, int b) {<br> return a + b;<br>}<br> | kotlin<br>fun add(a: Int, b: Int): Int {<br> return a + b<br>}<br> | 基本函数 |
| java<br>void print(String msg) {<br> System.out.println(msg);<br>}<br> | kotlin<br>fun print(msg: String) {<br> println(msg)<br>}<br> | 无返回值 |
| java<br>String getName() {<br> return this.name;<br>}<br> | kotlin<br>val name: String<br> get() = field<br> | Getter方法 |
五、类与继承对照
1. 类定义
java
// Java
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void sayHello() {
System.out.println("Hello, I'm " + name);
}
}
Kotlin
// Kotlin
class Person(val name: String, val age: Int) {
fun sayHello() {
println("Hello, I'm $name")
}
}
2. 继承
java
// Java
public class Student extends Person {
private String studentId;
public Student(String name, int age, String studentId) {
super(name, age);
this.studentId = studentId;
}
}
Kotlin
// Kotlin
open class Person(val name: String, val age: Int)
class Student(name: String, age: Int, val studentId: String)
: Person(name, age)
六、接口与实现
java
// Java
public interface Animal {
void eat();
void sleep();
}
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog is eating");
}
@Override
public void sleep() {
System.out.println("Dog is sleeping");
}
}
Kotlin
// Kotlin
interface Animal {
fun eat()
fun sleep()
}
class Dog : Animal {
override fun eat() {
println("Dog is eating")
}
override fun sleep() {
println("Dog is sleeping")
}
}
七、集合操作对照
|--------------------------------------------|--------------------------------------|------------|
| Java 操作 | Kotlin 操作 | 说明 |
| List<String> list = new ArrayList<>(); | val list = mutableListOf<String>() | 创建可变列表 |
| list.add("item"); | list.add("item") | 添加元素 |
| list.get(0); | list[0] | 获取元素 |
| for (String item : list) {...} | for (item in list) {...} | 遍历 |
| list.stream().filter(...) | list.filter {...} | 过滤 |
八、空安全对照
java
// Java (可能NPE)
String str = getString();
if (str != null) {
int length = str.length();
}
Kotlin
// Kotlin (空安全)
val str: String? = getString()
val length = str?.length // 安全调用
// 或使用 Elvis 操作符
val length = str?.length ?: 0
// 非空断言(谨慎使用)
val length = str!!.length
九、扩展函数(Kotlin特有)
Kotlin
// Kotlin 扩展函数(Java中没有对应功能)
fun String.addExclamation(): String {
return "$this!"
}
// 使用
val greeting = "Hello".addExclamation() // "Hello!"
十、数据类(Kotlin特有)
Kotlin
// Kotlin 数据类(替代Java的POJO)
data class User(
val id: Long,
val name: String,
val email: String
)
// 自动生成:equals(), hashCode(), toString(), copy(), componentN()
十一、单例模式对照
Kotlin
// Java 单例
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Kotlin
// Kotlin 单例(使用object关键字)
object Singleton {
fun doSomething() {
println("Singleton operation")
}
}
// 使用
Singleton.doSomething()
十二、实用转换技巧
1. 类型转换
Kotlin
// Java
Object obj = "123";
if (obj instanceof String) {
String str = (String) obj;
int num = Integer.parseInt(str);
}
Kotlin
// Kotlin
val obj: Any = "123"
if (obj is String) {
val num = obj.toInt() // 智能转换
}
2. 字符串模板
java
// Java
String message = "Hello, " + name + "! You are " + age + " years old.";
Kotlin
// Kotlin
val message = "Hello, $name! You are $age years old."
Java 转 Kotlin 详细语法对照表
一、基础语法对照
1. 变量与常量
|--------------------------------------|---------------------------|------------|
| Java | Kotlin | 说明 |
| int x = 5; | val x: Int = 5 | 不可变变量 |
| int y; y = 10; | var y: Int = 10 | 可变变量 |
| final int MAX = 100; | const val MAX = 100 | 编译时常量 |
| static final String NAME = "Kotlin"; | const val NAME = "Kotlin" | 静态常量 |
Kotlin
// 类型推断
val name = "Kotlin" // 自动推断为String
var count = 0 // 自动推断为Int
// 延迟初始化
lateinit var service: MyService
val lazyValue: String by lazy {
"Computed only once"
}
2. 空安全处理
|------------------------------------|-------------------------|------------|
| Java | Kotlin | 说明 |
| String str = null; | val str: String? = null | 可空类型 |
| if (str != null) { str.length(); } | str?.length | 安全调用 |
| str != null ? str : "default" | str ?: "default" | Elvis操作符 |
| Objects.requireNonNull(str) | str!! | 非空断言 |
Kotlin
// 安全调用链
val length = user?.address?.city?.length
// Elvis操作符
val name = nullableName ?: "Unknown"
// 安全转换
val number: Int? = str as? Int
二、控制结构对照
1. 条件表达式
|----------------------------------------------|-----------------------------|------------|
| Java | Kotlin | 说明 |
| if (a > b) { return a; } else { return b; } | return if (a > b) a else b | if表达式 |
| switch (x) { case 1: ... } | when (x) { 1 -> ... } | when表达式 |
Kotlin
// when表达式
val description = when (x) {
1 -> "One"
2, 3 -> "Two or Three"
in 4..10 -> "Between 4 and 10"
else -> "Other"
}
// when作为表达式
val result = when {
x > 0 -> "Positive"
x < 0 -> "Negative"
else -> "Zero"
}
2. 循环结构
|------------------------------|------------------------------|------------|
| Java | Kotlin | 说明 |
| for (int i=0; i<10; i++) | for (i in 0 until 10) | 范围循环 |
| for (String item : list) | for (item in list) | 集合遍历 |
| while (condition) { ... } | while (condition) { ... } | while循环 |
| do { ... } while (condition) | do { ... } while (condition) | do-while循环 |
Kotlin
// 带索引遍历
for ((index, value) in list.withIndex()) {
println("$index: $value")
}
// 步长循环
for (i in 10 downTo 1 step 2) {
println(i) // 10, 8, 6, 4, 2
}
// 遍历map
for ((key, value) in map) {
println("$key = $value")
}
三、函数/方法对照
1. 函数定义
|-------------------------------------------|---------------------------------------------------|------------|
| Java | Kotlin | 说明 |
| int add(int a, int b) { return a+b; } | fun add(a: Int, b: Int): Int = a + b | 单表达式函数 |
| void print(String msg) { ... } | fun print(msg: String): Unit { ... } | Unit返回类型 |
| String format(String fmt, Object... args) | fun format(fmt: String, vararg args: Any): String | 可变参数 |
Kotlin
// 默认参数
fun greet(name: String = "Guest") {
println("Hello, $name")
}
// 命名参数
greet(name = "Alice")
// 扩展函数
fun String.addExclamation(): String = "$this!"
// 中缀函数
infix fun Int.times(str: String) = str.repeat(this)
val result = 3 times "Hi" // "HiHiHi"
2. 高阶函数与Lambda
|-------------------------------------|----------------------|------------|
| Java | Kotlin | 说明 |
| interface Callback { void call(); } | () -> Unit | 函数类型 |
| list.stream().map(x -> x*2) | list.map { it * 2 } | Lambda表达式 |
Kotlin
// 函数类型
val sum: (Int, Int) -> Int = { a, b -> a + b }
// 带接收者的Lambda
val stringBuilder = StringBuilder().apply {
append("Hello")
append(" ")
append("World")
}
// 内联函数
inline fun measureTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
val end = System.currentTimeMillis()
println("Time: ${end - start}ms")
}
四、类与对象对照
1. 类定义
|----------------------------------------------|----------------------------------------|------------|
| Java | Kotlin | 说明 |
| public class Person { private String name; } | class Person(private val name: String) | 主构造函数 |
| new Person("Alice") | Person("Alice") | 对象创建 |
| this.name = name; | init { this.name = name } | 初始化块 |
Kotlin
// 主构造函数
class Person(val name: String, var age: Int) {
init {
require(age >= 0) { "Age cannot be negative" }
}
// 次构造函数
constructor(name: String) : this(name, 0)
}
// 数据类(自动生成equals/hashCode/toString/copy)
data class User(val id: Long, val name: String, val email: String)
// 密封类
sealed class Result {
data class Success(val data: Any) : Result()
data class Error(val message: String) : Result()
}
2. 继承与接口
|---------------------------------|-------------------------------|------------|
| Java | Kotlin | 说明 |
| class Child extends Parent | class Child : Parent() | 继承 |
| class A implements B, C | class A : B, C | 实现接口 |
| @Override void method() { ... } | override fun method() { ... } | 重写方法 |
Kotlin
// 抽象类
abstract class Shape {
abstract fun area(): Double
open fun draw() { println("Drawing shape") }
}
// 继承
class Circle(val radius: Double) : Shape() {
override fun area() = Math.PI * radius * radius
final override fun draw() { println("Drawing circle") }
}
// 接口默认实现
interface Clickable {
fun click()
fun showOff() = println("I'm clickable!")
}
五、属性与访问器
|----------------------------------------------------------------------------------------------------------------------|-----------------------|---------------------|
| Java | Kotlin | 说明 |
| private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } | var name: String = "" | 属性自动生成getter/setter |
| private final int id; public int getId() { return id; } | val id: Int | 只读属性 |
Kotlin
// 自定义getter/setter
class Rectangle(val width: Int, val height: Int) {
val area: Int
get() = width * height
var borderColor: String = "black"
set(value) {
field = if (value.isNotEmpty()) value else "black"
}
}
// 幕后字段
var counter = 0
set(value) {
if (value >= 0) field = value
}
六、集合操作对照
1. 集合创建
|-------------------------------------------------|-----------------------------------------|------------|
| Java | Kotlin | 说明 |
| List<String> list = new ArrayList<>(); | val list = mutableListOf<String>() | 可变列表 |
| Set<Integer> set = new HashSet<>(); | val set = mutableSetOf<Int>() | 可变集合 |
| Map<String, Integer> map = new HashMap<>(); | val map = mutableMapOf<String, Int>() | 可变映射 |
Kotlin
// 只读集合
val readOnlyList = listOf(1, 2, 3)
val readOnlySet = setOf("A", "B", "C")
val readOnlyMap
2. 集合操作
|--------------------|-----------------------------------|------------|
| Java | Kotlin | 说明 |
| list.add("item") | list.add("item") 或 list += "item" | 添加元素 |
| list.get(0) | list[0] | 获取元素 |
| list.set(0, "new") | list[0] = "new" | 设置元素 |
| list.size() | list.size | 集合大小 |
| list.isEmpty() | list.isEmpty() | 判空 |
Kotlin
// 集合操作符
val numbers = listOf(1, 2, 3, 4, 5)
// 过滤
val evens = numbers.filter { it % 2 == 0 } // [2, 4]
// 映射
val squares = numbers.map { it * it } // [1, 4, 9, 16, 25]
// 查找
val firstEven = numbers.find { it % 2 == 0 } // 2
// 分组
val grouped = numbers.groupBy { if (it % 2 == 0) "even" else "odd" }
// 排序
val sorted = numbers.sorted()
val sortedDesc = numbers.sortedDescending()
// 聚合
val sum = numbers.sum() // 15
val max = numbers.maxOrNull() // 5
val avg = numbers.average() // 3.0
3. 序列(Stream替代)
|-------------------------------|--------------------------------|------------|
| Java Stream | Kotlin Sequence | 说明 |
| list.stream().filter(...) | list.asSequence().filter {...} | 惰性求值 |
| .map(...) | .map {...} | 转换 |
| .collect(Collectors.toList()) | .toList() | 收集结果 |
| .limit(10) | .take(10) | 限制数量 |
| .skip(5) | .drop(5) | 跳过元素 |
Kotlin
// 序列操作(惰性求值)
val result = (1..1_000_000)
.asSequence()
.filter { it % 2 == 0 }
.map { it * 2 }
.take(10)
.toList()
七、异常处理对照
|-----------------------------------------|------------------------------------------|---------------|
| Java | Kotlin | 说明 |
| try { ... } catch (Exception e) { ... } | try { ... } catch (e: Exception) { ... } | 基本try-catch |
| try { ... } finally { ... } | try { ... } finally { ... } | finally块 |
| throw new Exception("error") | throw Exception("error") | 抛出异常 |
| throws IOException | 不需要声明 | Kotlin不强制检查异常 |
Kotlin
// try-catch表达式
val number = try {
"123".toInt()
} catch (e: NumberFormatException) {
0
}
// 使用runCatching
val result = runCatching {
"abc".toInt()
}.getOrElse {
0
}
// 多个catch块
try {
// 可能抛出异常的代码
} catch (e: IOException) {
println("IO错误: ${e.message}")
} catch (e: IllegalArgumentException) {
println("参数错误: ${e.message}")
} finally {
println("清理资源")
}
八、泛型对照
|---------------------------|-------------------------------|------------|
| Java | Kotlin | 说明 |
| List<String> | List<String> | 泛型类型 |
| T method(T param) | fun <T> method(param: T): T | 泛型方法 |
| class Box<T> | class Box<T> | 泛型类 |
| ? extends Number | out Number | 协变 |
| ? super Number | in Number | 逆变 |
| T extends Comparable<T> | T : Comparable<T> | 类型约束 |
Kotlin
// 泛型函数
fun <T> singletonList(item: T): List<T> = listOf(item)
// 泛型类
class Box<T>(val value: T)
// 类型约束
fun <T : Comparable<T>> sort(list: List<T>): List<T> = list.sorted()
// 多个约束
fun <T> cloneIfGreater(list: List<T>, threshold: T): List<T>
where T : Comparable<T>, T : Cloneable {
return list.filter { it > threshold }
}
// 星投影(相当于Java的?)
fun printList(list: List<*>) {
for (item in list) {
println(item)
}
}
九、注解与反射
|--------------------------------|-----------------------------|------------|
| Java | Kotlin | 说明 |
| @Override | override(关键字) | 重写方法 |
| @Deprecated | @Deprecated | 废弃标记 |
| @SuppressWarnings("unchecked") | @Suppress("UNCHECKED_CAST") | 抑制警告 |
| @Nullable | ?(可空类型) | 空值标记 |
| @NotNull | 非空类型(默认) | 非空标记 |
Kotlin
// 自定义注解
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Fancy(val name: String)
// 使用注解
@Fancy("MyClass")
class MyClass {
@Fancy("myMethod")
fun myMethod() {}
}
// Java注解在Kotlin中使用
@JvmStatic
@JvmOverloads
@Throws(IOException::class)
fun javaInteropMethod() { }
// 反射
val kClass = MyClass::class
val properties = kClass.memberProperties
val functions = kClass.memberFunctions
十、作用域函数(Kotlin特有)
|------------|---------------|-------------|--------------|
| 函数 | 上下文对象 | 返回值 | 使用场景 |
| let | it | Lambda结果 | 非空对象处理 |
| run | this | Lambda结果 | 对象配置和计算 |
| with | this | Lambda结果 | 调用对象方法 |
| apply | this | 对象本身 | 对象初始化 |
| also | it | 对象本身 | 附加操作 |
Kotlin
// let - 处理可空对象
val length = nullableString?.let {
println("Processing: $it")
it.length
} ?: 0
// run - 对象配置和计算
val result = service.run {
port = 8080
start()
query()
}
// with - 调用对象方法
with(window) {
width = 800
height = 600
isVisible = true
}
// apply - 对象初始化
val person = Person().apply {
name = "Alice"
age = 25
address = "New York"
}
// also - 附加操作
val numbers = mutableListOf(1, 2, 3)
.also { println("List created: $it") }
.apply { add(4) }
.also { println("After adding 4: $it") }
十一、协程(替代Java线程)
|----------------------------------|-------------------------------------|------------|
| Java 线程 | Kotlin 协程 | 说明 |
| new Thread(() -> {...}).start() | launch { ... } | 启动协程 |
| ExecutorService | CoroutineScope | 协程作用域 |
| Future<T> | Deferred<T> | 异步结果 |
| Thread.sleep(1000) | delay(1000) | 非阻塞延迟 |
| synchronized(obj) {...} | withContext(Dispatchers.Main) {...} | 线程切换 |
Kotlin
// 基本协程
GlobalScope.launch {
delay(1000L) // 非阻塞延迟
println("World!")
}
println("Hello,")
// 异步操作
suspend fun fetchUser(): User {
return withContext(Dispatchers.IO) {
// 网络请求
api.getUser()
}
}
// 并发执行
val user = async { fetchUser() }
val posts = async { fetchPosts() }
println("User: ${user.await()}, Posts: ${posts.await()}")
// 结构化并发
coroutineScope {
launch {
delay(200L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope")
}
// 取消协程
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L)
job.cancel()
十二、DSL构建器
Kotlin
// HTML DSL示例
fun
十二、DSL构建器(Kotlin特有)
|-------------------------------------------------------------|---------------------------------|------------|
| Java Builder模式 | Kotlin DSL | 说明 |
| new AlertDialog.Builder(context).setTitle("Title").create() | alertDialog { title = "Title" } | DSL构建器 |
| XML/JSON配置 | 类型安全的DSL | 配置构建 |
Kotlin
// HTML DSL示例
fun createHTML(): String = html {
head {
title { +"Kotlin DSL" }
}
body {
h1 { +"Welcome" }
p {
+"This is Kotlin DSL"
}
}
}
// 自定义DSL
class DatabaseConfig {
var url: String = ""
var username: String = ""
var password: String = ""
fun build() = "jdbc:$url?user=$username&password=$password"
}
fun database(block: DatabaseConfig.() -> Unit): String {
val config = DatabaseConfig()
config.block()
return config.build()
}
// 使用
val connection = database {
url = "mysql://localhost:3306/mydb"
username = "admin"
password = "secret"
}
十三、委托(Kotlin特有)
|----------------|------------------|------------|
| Java模式 | Kotlin委托 | 说明 |
| 装饰器模式 | 类委托 | 实现继承的替代 |
| 观察者模式 | 属性委托 | 属性变化监听 |
| 懒加载模式 | lazy委托 | 延迟初始化 |
Kotlin
// 类委托
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { println(x) }
}
class Derived(b: Base) : Base by b // 将b的方法委托给Derived
// 属性委托
class Delegate {
private var value: String = ""
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("Getting value: $value")
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("Setting value: $value")
this.value = value
}
}
class Example {
var p: String by Delegate()
}
// 使用
val e = Example()
e.p = "Hello" // 输出: Setting value: Hello
println(e.p) // 输出: Getting value: Hello 然后输出: Hello
// 标准库委托
class User {
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
var observableValue: String by Delegates.observable("初始值") {
prop, old, new ->
println("$old -> $new")
}
var vetoableValue: Int by Delegates.vetoable(0) {
prop, old, new ->
new >= 0 // 只有新值>=0时才更新
}
}
十四、类型检查与转换
|--------------------------------------------------------|--------------------------------------------|------------|
| Java | Kotlin | 说明 |
| obj instanceof String | obj is String | 类型检查 |
| (String) obj | obj as String | 强制转换 |
| if (obj instanceof String) { String s = (String)obj; } | if (obj is String) { println(obj.length) } | 智能转换 |
| Class<?> clazz = obj.getClass() | val clazz = obj::class | 获取类对象 |
Kotlin
// 类型检查与转换
val obj: Any = "Hello"
// 安全转换
val str1: String? = obj as? String // 转换失败返回null
// 强制转换
val str2: String = obj as String // 转换失败抛出异常
// 智能转换
if (obj is String) {
println(obj.length) // obj自动转换为String
}
// when表达式中的智能转换
when (obj) {
is String -> println(obj.length)
is Int -> println(obj + 1)
is List<*> -> println(obj.size)
}
// 泛型类型检查
fun <T> checkType(value: Any): T? {
return if (value is T) value else null // 注意:这里不能工作,因为T被擦除了
}
// 使用内联函数和reified类型参数
inline fun <reified T> checkTypeReified(value: Any): T? {
return if (value is T) value else null
}
val result: String? = checkTypeReified("Hello") // 可以工作
十五、伴生对象与静态成员
|------------------------------|-------------------------------------------|------------|
| Java静态成员 | Kotlin对应 | 说明 |
| static final int MAX = 100; | const val MAX = 100 | 编译时常量 |
| static int count = 0; | companion object { var count = 0 } | 伴生对象属性 |
| static void method() { ... } | companion object { fun method() { ... } } | 伴生对象方法 |
| MyClass.class | MyClass::class | 类字面量 |
Kotlin
class MyClass {
companion object {
const val VERSION = "1.0"
var instanceCount = 0
fun create(): MyClass {
instanceCount++
return MyClass()
}
@JvmStatic // 让Java可以像调用静态方法一样调用
fun staticMethod() {
println("This is a static-like method")
}
}
init {
instanceCount++
}
}
// 使用
println(MyClass.VERSION) // 1.0
val obj = MyClass.create()
println(MyClass.instanceCount) // 2
// 伴生对象可以有名字
class MyClass2 {
companion object Factory {
fun create(): MyClass2 = MyClass2()
}
}
// 实现接口的伴生对象
interface Factory<T> {
fun create(): T
}
class MyClass3 {
companion object : Factory<MyClass3> {
override fun create(): MyClass3 = MyClass3()
}
}
十六、内联类(值类)
|----------------------------------------------------|----------------------------------------------------|------------|
| Java包装类 | Kotlin内联类 | 说明 |
| public class Email { private final String value; } | inline class Email(val value: String) | 类型安全的包装 |
| Integer.valueOf(42) | @JvmInline value class Password(val value: String) | 运行时无开销 |
Kotlin
// 内联类(Kotlin 1.3+)
@JvmInline
value class Password(val value: String) {
init {
require(value.length >= 8) { "密码至少8位" }
}
fun isStrong(): Boolean = value.any { it.isDigit() }
&& value.any { it.isLetter() }
}
// 使用
fun authenticate(password: Password) {
println("验证密码: ${password.value}")
println("强度: ${if (password.isStrong()) "强" else "弱"}")
}
val myPassword = Password("Secure123")
authenticate(myPassword)
// 多个内联类
@JvmInline
value class UserId(val id: Long)
@JvmInline
value class ProductId(val id: Long)
fun getUser(id: UserId) { /* ... */ }
fun getProduct(id: ProductId) { /* ... */ }
// 编译时类型安全,运行时无开销
val userId = UserId(123L)
val productId = ProductId(456L)
getUser(userId) // OK
getUser(productId) // 编译错误:类型不匹配
十七、解构声明
|----------------|------------------|------------|
| Java解构 | Kotlin解构 | 说明 |
| 需要手动提取 | 自动解构 | 从对象中提取多个值 |
Kotlin
// 数据类的解构
data class Person(val name: String, val age: Int, val city: String)
val person = Person("Alice", 25, "New York")
// 解构声明
val (name, age, city) = person
println("$name is $age years old from $city")
// 忽略某些组件
val (_, ageOnly, _) = person
println("Age: $ageOnly")
// Map的解构
val map = mapOf("name" to "Bob", "age" to 30)
for ((key, value) in map) {
println("$key = $value")
}
// 函数返回多个值
data class Result(val success: Boolean, val message: String)
fun process(): Result {
return Result(true, "操作成功")
}
val (success, message) = process()
if (success) {
println(message)
}
// 自定义解构
class Point(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
}
val point = Point(10, 20)
val (x, y) = point
十八、操作符重载
|------------------|---------------------|------------|
| Java 操作符 | Kotlin 对应 | 说明 |
| a + b | a.plus(b) | 加法 |
| a - b | a.minus(b) | 减法 |
| a * b | a.times(b) | 乘法 |
| a / b | a.div(b) | 除法 |
| a % b | a.rem(b) | 取余 |
| a++ | a.inc() | 自增 |
| a-- | a.dec() | 自减 |
| a == b | a.equals(b) | 相等 |
| a > b | a.compareTo(b) > 0 | 比较 |
| a[i] | a.get(i) | 索引访问 |
| a[i] = b | a.set(i, b) | 索引赋值 |
Kotlin
// 自定义操作符重载
data class Point(val x: Int, val y: Int) {
// 重载 + 运算符
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
// 重载 - 运算符
operator fun minus(other: Point): Point {
return Point(x - other.x, y - other.y)
}
// 重载 * 运算符
operator fun times(factor: Int): Point {
return Point(x * factor, y * factor)
}
// 重载 == 运算符(数据类自动生成)
// 重载比较运算符
operator fun compareTo(other: Point): Int {
return (x + y).compareTo(other.x + other.y)
}
// 重载一元运算符
operator fun unaryMinus(): Point {
return Point(-x, -y)
}
}
// 使用
val p1 = Point(1, 2)
val p2 = Point(3, 4)
val p3 = p1 + p2 // Point(4, 6)
val p4 = p2 - p1 // Point(2, 2)
val p5 = p1 * 3 // Point(3, 6)
val p6 = -p1 // Point(-1, -2)
if (p1 < p2) {
println("p1 is less than p2")
}
// 索引访问操作符重载
class Matrix(val rows: Int, val cols: Int) {
private val data = Array(rows) { DoubleArray(cols) }
operator fun get(row: Int, col: Int): Double {
return data[row][col]
}
operator fun set(row: Int, col: Int, value: Double) {
data[row][col] = value
}
}
val matrix = Matrix(3, 3)
matrix[0, 0] = 1.0
println(matrix[0, 0]) // 1.0
// 调用操作符重载
class Greeter(val prefix: String) {
operator fun invoke(name: String) {
println("$prefix, $name!")
}
}
val hello = Greeter("Hello")
hello("World") // 输出: Hello, World!
十九、中缀函数
|-------------------|---------------------|------------|
| Java 方法调用 | Kotlin 中缀调用 | 说明 |
| obj.method(arg) | obj method arg | 中缀表示法 |
Kotlin
// 定义中缀函数
infix fun Int.add(other: Int): Int = this + other
infix fun String.should(startWith: String): Boolean {
return this.startsWith(startWith)
}
// 使用中缀函数
val result = 5 add 3 // 等价于 5.add(3)
println(result) // 8
val isValid = "Hello" should "He"
println(isValid) // true
// 自定义中缀函数用于DSL
class Database {
infix fun select(columns: String): QueryBuilder {
return QueryBuilder(this, columns)
}
}
class QueryBuilder(val db: Database, val columns: String) {
infix fun from(table: String): QueryBuilder {
println("SELECT $columns FROM $table")
return this
}
infix fun where(condition: String): QueryBuilder {
println("WHERE $condition")
return this
}
}
// 创建DSL风格的查询
val db = Database()
db select "*" from "users" where "age > 18"
// 范围中缀函数
val range = 1..10 // 使用中缀表示法
val range2 = 1.rangeTo(10) // 等价写法
val rangeUntil = 1 until 10 // 1..9
val charRange = 'a'..'z'
// 检查包含关系
if (5 in 1..10) {
println("5在1到10之间")
}
二十、类型别名
|----------------------------------------------|----------------------------------------------------------------|------------|
| Java 冗长类型 | Kotlin 类型别名 | 说明 |
| Map<String, List<Map<String, Object>>> | typealias UserData = Map<String, List<Map<String, Any>>> | 简化复杂类型 |
Kotlin
// 类型别名
typealias UserId = String
typealias UserMap = Map<UserId, User>
typealias ClickHandler = (View) -> Unit
typealias Predicate<T> = (T) -> Boolean
// 使用类型别名
data class User(val id: UserId, val name: String)
fun processUsers(users: UserMap) {
for ((id, user) in users) {
println("User $id: ${user.name}")
}
}
// 函数类型别名
typealias StringFilter = (String) -> Boolean
fun filterStrings(strings: List<String>, filter: StringFilter): List<String> {
return strings.filter(filter)
}
val strings = listOf("apple", "banana", "cherry")
val longStrings = filterStrings(strings) { it.length > 5 }
// 泛型类型别名
typealias StringMap<V> = Map<String, V>
typealias ListOrSet<T> = List<T> or Set<T> // Kotlin 1.6+ 支持
// 类类型别名
typealias FileTable = MutableMap<Int, MutableList<File>>
// 内部类类型别名
class Outer {
inner class Inner
}
typealias OuterInner = Outer.Inner
// 与扩展函数结合
typealias IntPredicate = (Int) -> Boolean
fun Int.isEven(): Boolean = this % 2 == 0
val isEven: IntPredicate = Int::isEven
println(isEven(4)) // true
二十一、密封类与枚举增强
|---------------------|---------------------|------------|
| Java枚举/模式匹配 | Kotlin密封类 | 说明 |
| enum + switch | sealed class + when | 更强大的枚举 |
| Visitor模式 | 密封类层次结构 | 类型安全的模式匹配 |
Kotlin
// 密封类(受限的类层次结构)
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
// 使用when表达式(完全覆盖所有情况)
fun handleResult(result: Result<String>) {
when (result) {
is Result.Success -> println("成功: ${result.data}")
is Result.Error -> println("错误: ${result.exception.message}")
Result.Loading -> println("加载中...")
// 不需要else分支,因为所有情况都已覆盖
}
}
// 更复杂的密封类层次
sealed class Expression {
data class Number(val value: Int) : Expression()
data class Sum(val left: Expression, val right: Expression) : Expression()
data class Multiply(val left: Expression, val right: Expression) : Expression()
}
fun evaluate(expr: Expression): Int = when (expr) {
is Expression.Number -> expr.value
is Expression.Sum -> evaluate(expr.left) + evaluate(expr.right)
is Expression.Multiply -> evaluate(expr.left) * evaluate(expr.right)
}
// 枚举类增强
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF);
fun containsRed(): Boolean = (this.rgb and 0xFF0000 != 0)
}
// 枚举与when结合
fun getColorDescription(color: Color): String = when (color) {
Color.RED -> "热情的颜色"
Color.GREEN -> "自然的颜色"
Color.BLUE -> "冷静的颜色"
}
// 密封类与枚举的区别
sealed class PaymentMethod
data class CreditCard(val number: String, val expiry: String) : PaymentMethod()
data class PayPal(val email: String) : PaymentMethod()
object Cash : PaymentMethod()
// 枚举适用于固定值,密封类适用于有数据的变体
enum
二十二、内联函数与具体化类型参数
|-------------------|---------------------|------------|
| Java 泛型限制 | Kotlin 解决方案 | 说明 |
| 类型擦除 | reified 类型参数 | 运行时保留类型信息 |
| 高阶函数性能开销 | inline 函数 | 消除函数调用开销 |
Kotlin
// 内联函数消除lambda开销
inline fun <T> measureTimeMillis(block: () -> T): T {
val start = System.currentTimeMillis()
val result = block()
val end = System.currentTimeMillis()
println("Execution time: ${end - start}ms")
return result
}
// 使用
val result = measureTimeMillis {
// 耗时操作
(1..1000000).sum()
}
// 具体化类型参数
inline fun <reified T> checkType(value: Any): Boolean {
return value is T
}
inline fun <reified T> List<*>.filterIsInstance(): List<T> {
return filter { it is T }.map { it as T }
}
// 使用
val list: List<Any> = listOf(1, "hello", 2, "world")
val strings: List<String> = list.filterIsInstance<String>() // ["hello", "world"]
val numbers: List<Int> = list.filterIsInstance<Int>() // [1, 2]
// 内联属性
inline val <T> List<T>.lastIndex: Int
get() = size - 1
// noinline 和 crossinline
inline fun doSomething(
crossinline onSuccess: () -> Unit,
noinline onError: (Exception) -> Unit
) {
try {
// ...
onSuccess()
} catch (e: Exception) {
onError(e)
}
}
二十三、委托属性进阶
Kotlin
// 自定义属性委托
class RangeDelegate(
private val start: Int,
private val endInclusive: Int
) {
private var value: Int = start
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
println("Getting value: $value")
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: Int) {
println("Setting value: $newValue")
require(newValue in start..endInclusive) {
"Value must be between $start and $endInclusive"
}
value = newValue
}
}
class Person {
var age: Int by RangeDelegate(0, 150)
var score: Int by RangeDelegate(0, 100)
}
// 使用
val person = Person()
person.age = 25 // OK
person.age = 200 // 抛出异常
// Map委托
class Config(properties: Map<String, Any>) {
val host: String by properties
val port: Int by properties
val debug: Boolean by properties
}
val config = Config(mapOf(
"host" to "localhost",
"port" to 8080,
"debug" to true
))
// 提供者委托
fun <T> provideDelegate(
thisRef: Any?,
property: KProperty<*>
): ReadOnlyProperty<Any?, T> {
// 可以在这里进行验证或初始化
println("Initializing ${property.name}")
return object : ReadOnlyProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
// 返回默认值或计算值
return "Default" as T
}
}
}
二十四、多平台项目(Kotlin Multiplatform)
|--------------------|------------------------------|------------|
| Java 跨平台方案 | Kotlin Multiplatform | 说明 |
| 共享JAR库 | 共享Kotlin代码 | 真正的代码共享 |
| 平台特定实现 | expect/actual | 声明期望和实际实现 |
Kotlin
// 公共模块 - commonMain
expect class Platform() {
val platform: String
}
expect fun currentTimeMillis(): Long
class Greeting {
fun greet(): String {
return "Hello from ${Platform().platform} at ${currentTimeMillis()}"
}
}
// Android实现 - androidMain
actual class Platform actual constructor() {
actual val platform: String = "Android"
}
actual fun currentTimeMillis(): Long = System.currentTimeMillis()
// iOS实现 - iosMain
import platform.Foundation.NSDate
import platform.Foundation.timeIntervalSince1970
actual class Platform actual constructor() {
actual val platform: String = "iOS"
}
actual fun currentTimeMillis(): Long =
(NSDate().timeIntervalSince1970 * 1000).toLong()
// JVM实现 - jvmMain
actual class Platform actual constructor() {
actual val platform: String = "JVM"
}
actual fun currentTimeMillis(): Long = System.currentTimeMillis()
二十五、协程流(Flow)
|-------------------|---------------------|------------|
| Java 响应式流 | Kotlin Flow | 说明 |
| RxJava/Observable | Flow | 冷流,更轻量 |
| Stream | Flow | 异步数据流 |
Kotlin
// 创建Flow
fun simpleFlow(): Flow<Int> = flow {
for (i in 1..3) {
delay(100) // 模拟异步操作
emit(i) // 发射值
}
}
// 收集Flow
fun main() = runBlocking {
simpleFlow()
.map { it * it } // 转换
.filter { it % 2 != 0 } // 过滤
.collect { value -> // 收集
println(value)
}
}
// 状态流
val stateFlow = MutableStateFlow(0)
// 共享流
val sharedFlow = MutableSharedFlow<Int>()
// Flow操作符
fun processFlow(): Flow<String> = flow {
emit("Apple")
emit("Banana")
emit("Cherry")
}
fun main() = runBlocking {
processFlow()
.onEach { println("Processing: $it") }
.map { it.uppercase() }
.catch { e -> emit("Error: ${e.message}") }
.onCompletion { println("Completed") }
.collect { println("Result: $it") }
}
// 与LiveData集成(Android)
fun <T> Flow<T>.asLiveData(): LiveData<T> = liveData {
collect { value ->
emit(value)
}
}
二十六、上下文接收者(Context Receivers) - Kotlin 1.6.20+
Kotlin
// 定义上下文
interface LoggingContext {
val logger: Logger
}
interface DatabaseContext {
val database: Database
}
// 上下文接收者函数
context(LoggingContext)
fun logInfo(message: String) {
logger.info(message)
}
context(LoggingContext, DatabaseContext)
fun processUser(userId: String) {
logInfo("Processing user: $userId")
val user = database.findUser(userId)
// 处理用户
}
// 提供上下文
class ApplicationContext : LoggingContext, DatabaseContext {
override val logger = Logger.getLogger("App")
override val database = Database.connect()
}
// 使用
fun main() {
val context = ApplicationContext()
with(context) {
processUser("123")
}
}
二十七、挂起函数与回调转换
|--------------------------|---------------------|------------|
| Java 回调模式 | Kotlin 挂起函数 | 说明 |
| 回调地狱 | 顺序代码 | 异步代码同步写 |
| Future/CompletableFuture | suspend函数 | 更简洁的异步 |
Kotlin
// 将回调转换为挂起函数
suspend fun fetchUser(userId: String): User = suspendCoroutine { continuation ->
api.getUser(userId, object : Callback<User> {
override fun onSuccess(user: User) {
continuation.resume(user)
}
override fun onError(error: Throwable) {
continuation.resumeWithException(error)
}
})
}
// 使用回调转换器
fun <T> Callback<T>.asSuspend(): suspend () -> T = suspendCoroutine { cont ->
this.enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
cont.resume(response.body()!!)
} else {
cont.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
cont.resumeWithException(t)
}
})
}
// 顺序执行异步操作
suspend fun loadUserData(userId: String): UserData {
// 顺序执行,但不会阻塞线程
val user = fetchUser(userId)
val posts = fetchPosts(userId)
val friends = fetchFriends(userId)
return UserData(user, posts, friends)
}
// 并发执行异步操作
suspend fun loadUserDataConcurrent(userId: String): UserData = coroutineScope {
val userDeferred = async {
二十八、反射与元编程
|-----------------------------------|--------------------------|------------|
| Java 反射 | Kotlin 反射 | 说明 |
| Class<?> clazz = obj.getClass() | val kClass = obj::class | 获取KClass |
| clazz.getMethod("name") | kClass.functions | 获取函数 |
| clazz.getField("field") | kClass.memberProperties | 获取属性 |
| method.invoke(obj, args) | function.call(obj, args) | 调用函数 |
Kotlin
// 基本反射
data class Person(val name: String, val age: Int)
fun reflectExample() {
val person = Person("Alice", 25)
// 获取KClass
val kClass = person::class
println("Class name: ${kClass.simpleName}")
// 获取属性
val properties = kClass.memberProperties
for (property in properties) {
println("Property: ${property.name} = ${property.get(person)}")
}
// 获取函数
val functions = kClass.functions
for (function in functions) {
println("Function: ${function.name}")
}
// 调用函数
val toStringMethod = kClass.functions.find { it.name == "toString" }
val result = toStringMethod?.call(person)
println("toString result: $result")
}
// 注解处理
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class JsonName(val name: String)
@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude
data class User(
@JsonName("user_name")
val name: String,
@JsonExclude
val password: String,
val email: String
)
fun serialize(obj: Any): String {
val kClass = obj::class
val properties = kClass.memberProperties
return properties
.filter { property ->
property.findAnnotation<JsonExclude>() == null
}
.joinToString(", ") { property ->
val jsonName = property.findAnnotation<JsonName>()?.name ?: property.name
val value = property.get(obj)
"\"$jsonName\": \"$value\""
}
.let { "{ $it }" }
}
// 动态创建实例
fun createInstance(kClass: KClass<*>, vararg args: Any?): Any? {
val constructor = kClass.constructors.firstOrNull()
return constructor?.call(*args)
}
// 使用
val personClass = Person::class
val newPerson = createInstance(personClass, "Bob", 30) as Person
二十九、DSL构建器模式
Kotlin
// HTML DSL构建器
interface Element {
fun render(builder: StringBuilder, indent: String)
}
class TextElement(val text: String) : Element {
override fun render(builder: StringBuilder, indent: String) {
builder.append("$indent$text\n")
}
}
class HtmlElement(val name: String) : Element {
private val children = mutableListOf<Element>()
private val attributes = mutableMapOf<String, String>()
fun setAttribute(name: String, value: String) {
attributes[name] = value
}
fun child(init: HtmlElement.() -> Unit) {
val child = HtmlElement("div")
child.init()
children.add(child)
}
fun text(text: String) {
children.add(TextElement(text))
}
override fun render(builder: StringBuilder, indent: String) {
builder.append("$indent<$name")
for ((attrName, attrValue) in attributes) {
builder.append(" $attrName=\"$attrValue\"")
}
if (children.isEmpty()) {
builder.append("/>\n")
} else {
builder.append(">\n")
for (child in children) {
child.render(builder, "$indent ")
}
builder.append("$indent</$name>\n")
}
}
}
fun html(init: HtmlElement.() -> Unit): HtmlElement {
val html = HtmlElement("html")
html.init()
return html
}
// 使用DSL
val page = html {
setAttribute("lang", "en")
child {
name = "head"
child {
name = "title"
text("My Page")
}
}
child {
name = "body"
child {
name = "h1"
text("Welcome to Kotlin DSL")
}
child {
name = "p"
setAttribute("class", "content")
text("This is a paragraph.")
}
}
}
// 数据库查询DSL
class QueryBuilder {
private var select: String = "*"
private var from: String = ""
private var where: String = ""
private var orderBy: String = ""
fun select(columns: String) {
select = columns
}
fun from(table: String) {
from = table
}
fun where(condition: String) {
where = "WHERE $condition"
}
fun orderBy(column: String) {
orderBy = "ORDER BY $column"
}
fun build(): String {
return "SELECT $select FROM $from $where $orderBy".trim()
}
}
fun query(init: QueryBuilder.() -> Unit): String {
val builder = QueryBuilder()
builder.init()
return builder.build()
}
// 使用
val sql = query {
select("name, age")
from("users")
where("age > 18")
orderBy("name")
}
// SELECT name, age FROM users WHERE age > 18 ORDER BY name
三十、Kotlin与Java互操作
1. 在Kotlin中调用Java代码
Kotlin
// Java类
// public class JavaClass {
// public static String staticMethod() { return "Hello"; }
// public String instanceMethod() { return "World"; }
// }
// Kotlin调用
val staticResult = JavaClass.staticMethod()
val instance = JavaClass()
val instanceResult = instance.instanceMethod()
// 处理空安全
@Nullable
public String getNullableString() { return null; }
@NotNull
public String getNotNullString() { return "not null"; }
// Kotlin中
val nullable: String? = javaClass.nullableString // 可空类型
val notNull: String = javaClass.notNullString // 非空类型
// SAM转换(单抽象方法接口)
// Java接口
// interface OnClickListener {
// void onClick(View v);
// }
// Kotlin使用
view.setOnClickListener { v ->
println("Clicked!")
}
// 使用Java流
val javaList = java.util.ArrayList<String>()
javaList.add("Java")
javaList.add("Kotlin")
val kotlinList = javaList.stream()
.filter { it.startsWith("K") }
.collect(Collectors.toList())
2. 在Java中调用Kotlin代码
java
// Kotlin文件
@file:JvmName("KotlinUtils") // 指定类名
object StringUtils {
@JvmStatic // 生成静态方法
fun capitalize(str: String): String {
return str.replaceFirstChar { it.uppercase() }
}
@JvmOverloads // 生成重载方法
fun join(separator: String = ", ", vararg parts: String): String {
return parts.joinToString(separator)
}
}
class User @JvmOverloads constructor(
val name: String,
val age: Int = 0,
val email: String = ""
) {
companion object {
@JvmField // 暴露为Java字段
val DEFAULT_NAME = "Guest"
@JvmStatic
fun createDefault() = User(DEFAULT_NAME)
}
}
java
// Java中调用
// 调用顶层函数
String result = KotlinUtils.capitalize("hello");
// 调用带默认参数的函数
String joined1 = StringUtils.join(", ", "a", "b", "c");
String joined2 = StringUtils.join("a", "b", "c"); // 使用默认分隔符
// 创建对象
User user1 = new User("Alice", 25, "alice@example.com");
User user2 = new User("Bob"); // 使用默认参数
// 访问伴生对象
String defaultName = User.DEFAULT_NAME;
User defaultUser = User.createDefault();
三十一、性能优化技巧
1. 内联函数优化
Kotlin
// 适合内联的情况
inline fun <T> synchronized(lock: Any, block: () -> T): T {
synchronized(lock) {
return block()
}
}
// 不适合内联的情况(函数体大或递归)
fun processLargeData(data: List<Int>, processor: (Int) -> Int): List<Int> {
return data.map(processor) // 不要内联,因为map本身是内联的
}
2. 集合操作优化
Kotlin
// 使用序列进行惰性求值(大数据集)
val largeList = (1..1_000_000).toList()
// 链式操作 - 创建多个中间集合
val result1 = largeList
.filter { it % 2 == 0 } // 创建中间集合1
.map { it * 2 } // 创建中间集合2
.take(1000) // 创建中间集合3
.toList()
// 使用序列 - 无中间集合,惰性计算
val result2 = largeList.asSequence()
.filter { it % 2 == 0 } // 惰性过滤
.map { it * 2 } // 惰性映射
.take(1000) // 只取前1000个
.toList() // 最终结果
// 使用数组处理基本类型(避免装箱开销)
val intArray = IntArray(1000) { it } // 基本类型int数组
val integerList = List(1000) { it } // 装箱的Integer列表
// 性能对比
fun benchmark() {
val size = 10_000_000
// 使用List(装箱)
val list = List(size) { it }
val time1 = measureTimeMillis {
val sum = list.sum()
}
// 使用IntArray(基本类型)
val array = IntArray(size) { it }
val time2 = measureTimeMillis {
val sum = array.sum()
}
println("List time: $time1 ms")
println("IntArray time: $time2 ms")
}
3. 字符串处理优化
Kotlin
// 避免在循环中使用字符串连接
fun slowConcat(list: List<String>): String {
var result = ""
for (item in list) {
result += item // 每次创建新字符串对象
}
return result
}
fun fastConcat(list: List<String>): String {
return StringBuilder().apply {
for (item in list) {
append(item) // 使用StringBuilder
}
}.toString()
}
fun fastestConcat(list: List<String>): String {
return list.joinToString("") // 使用标准库函数
}
// 使用字符串模板 vs 连接
val name = "Alice"
val age = 25
// 好:使用模板
val message1 = "Name: $name, Age: $age"
// 不好:使用连接
val message2 = "Name: " + name + ", Age: " + age
4. 内存优化
Kotlin
// 使用value class减少内存分配
@JvmInline
value class UserId(val value: Long) // 运行时作为基本类型
@JvmInline
value class Email(val value: String) // 编译时类型安全,运行时无开销
// 使用数组存储大量相同类型对象
data class Point(val x: Double, val y: Double)
// 不好:List<Point>,每个Point都是独立对象
val pointsList = List(1000000) { Point(it.toDouble(), it.toDouble()) }
// 好:使用平行数组
val xArray = DoubleArray(1000000) { it.toDouble() }
val yArray = DoubleArray(1000000) { it.toDouble() }
// 使用缓存避免重复计算
private val cache = mutableMapOf<Int, BigInteger>()
fun fibonacci(n: Int): BigInteger = cache.getOrPut(n) {
if (n <= 1) BigInteger.valueOf(n.toLong())
else fibonacci(n - 1) + fibonacci(n - 2)
}
三十二、Kotlin最佳实践
1. 代码风格指南
Kotlin
// 1. 使用val代替var(不可变性)
val immutableList = listOf(1, 2, 3) // 推荐
var mutableList = mutableListOf(1, 2, 3) // 只在需要时使用
// 2. 使用数据类替代POJO
data class User(val id: Long, val name: String, val email: String) // 推荐
// 自动生成equals, hashCode, toString, copy, componentN
// 3. 使用默认参数代替重载
fun connect(
host: String = "localhost",
port: Int = 8080,
timeout: Int = 5000
) { /* ... */ }
// 4. 使用命名参数提高可读性
connect(
host = "example.com",
port = 443,
timeout = 10000
)
// 5. 使用扩展函数增强可读性
fun String.isEmail(): Boolean =
this.matches(Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}$"))
// 使用
if (email.isEmail()) { /* ... */ }
// 6. 使用when表达式代替复杂的if-else
fun describeNumber(n: Int): String = when {
n < 0 -> "负数"
n == 0 -> "零"
n in 1..10 -> "小的正数"
else -> "大的正数"
}
// 7. 使用作用域函数简化代码
// 使用apply进行对象配置
val dialog = AlertDialog.Builder(context).apply {
setTitle("提示")
setMessage("确定要删除吗?")
setPositiveButton("确定") { _, _ -> /* ... */ }
setNegativeButton("取消", null)
}.create()
// 使用let处理可空对象
val length = nullableString?.let {
println("Processing: $it")
it.length
} ?: 0
2. 错误处理最佳实践
Kotlin
// 1. 使用Result类型处理可能失败的操作
fun parseNumber(str: String): Result<Int> = runCatching {
str.toInt()
}
// 使用
val result = parseNumber("123")
result.onSuccess { number ->
println("Parsed number: $number")
}.onFailure { error ->
println("Failed to parse: ${error.message}")
}
// 2. 使用密封类表示操作结果
sealed class ApiResult<out T> {
data class Success<T>(val data: T) : ApiResult<T>()
data class Error(val message: String, val code: Int) : ApiResult<Nothing>()
object Loading : ApiResult<Nothing>()
}
// 3. 使用require/check进行参数验证
fun divide(a: Int, b: Int): Int {
require(b != 0) { "除数不能为零" }
check(a >= 0) { "被除数必须非负" }
return a / b
}
// 4. 使用also进行调试和日志记录
val processedData = data
.also { println("原始数据: $it") }
.map { it * 2 }
.also { println("处理后的数据: $it") }
3. 协程最佳实践
Kotlin
// 1. 使用结构化并发
suspend fun fetchUserData(userId: String): UserData = coroutineScope {
val userDeferred = async { api.getUser(userId) }
val postsDeferred = async { api.getPosts(userId) }
UserData(
user = userDeferred.await(),
posts = postsDeferred.await()
)
}
// 2. 正确处理取消
suspend fun longRunningTask() = withContext(Dispatchers.IO) {
repeat(1000) { i ->
ensureActive() // 检查协程是否被取消
delay(100)
println("Processing $i")
}
}
// 3. 使用SupervisorJob处理子协程失败
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
scope.launch {
// 即使这个子协程失败,其他子协程仍会继续
throw RuntimeException("Failed!")
}
scope.launch {
// 这个协程会继续执行
delay(1000)
println("Still running")
}
// 4. 使用适当的调度器
suspend fun processData() {
// CPU密集型操作
withContext(Dispatchers.Default) {
// 计算、排序、转换等
}
// IO操作
withContext(Dispatchers.IO) {
// 文件读写、网络请求等
}
// UI更新
withContext(Dispatchers.Main) {
// 更新UI
}
}
三十三、测试与调试
1. 单元测试
Kotlin
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
class CalculatorTest {
private lateinit var calculator: Calculator
@BeforeEach
fun setUp() {
calculator = Calculator()
}
@Test
@DisplayName("测试加法")
fun testAdd() {
// 给定
val a = 5
val b = 3
// 当
val result = calculator.add(a, b)
// 那么
assertEquals(8, result)
}
@Test
@DisplayName("测试除以零")
fun testDivideByZero() {
// 给定
val a = 10
val b = 0
// 当/那么
assertThrows<ArithmeticException> {
calculator.divide(a, b)
}
}
@ParameterizedTest
@CsvSource(
"2, 3, 5",
"0, 0, 0",
"-5, 10, 5",
"100, -50, 50"
)
fun `parameterized test for addition`(a: Int, b: Int, expected: Int) {
assertEquals(expected, calculator.add(a, b))
}
@Test
fun `test with mock`() {
// 创建mock对象
val repository = mockk<UserRepository>()
val service = UserService(repository)
// 设置mock行为
every { repository.findById(1) } returns User(1, "Alice")
every { repository.findById(2) } returns null
// 执行测试
val user1 = service.getUser(1)
val user2 = service.getUser(2)
// 验证
assertEquals("Alice", user1?.name)
assertNull(user2)
verify(exactly = 2) { repository.findById(any()) }
}
}
// 使用Kotest进行更地道的Kotlin测试
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.arbitrary.int
import io.kotest.property.checkAll
class CalculatorSpec : StringSpec({
"加法应该返回正确结果" {
val calculator = Calculator()
calculator.add(2, 3) shouldBe 5
calculator.add(-1, 1) shouldBe 0
}
"除以零应该抛出异常" {
val calculator = Calculator()
shouldThrow<ArithmeticException> {
calculator.divide(10, 0)
}
}
"属性测试:加法交换律" {
checkAll(Arb.int(), Arb.int()) { a, b ->
val calculator = Calculator()
calculator.add(a, b) shouldBe calculator.add(b, a)
}
}
})
2. Android测试
Kotlin
// Android单元测试
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.app", appContext.packageName)
}
}
// Android UI测试
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
@Test
fun testButtonClick() {
// 检查初始状态
onView(withId(R.id.textView))
.check(matches(withText("Hello World!")))
// 执行点击
onView(withId(R.id.button))
.perform(click())
// 验证结果
onView(withId(R.id.textView))
.check(matches(withText("Button Clicked!")))
}
}
// ViewModel测试
@ExperimentalCoroutinesApi
class UserViewModelTest {
private lateinit var viewModel: UserViewModel
private lateinit var userRepository: UserRepository
@get:Rule
var mainCoroutineRule = MainCoroutineRule()
@Before
fun setup() {
userRepository = mockk()
viewModel = UserViewModel(userRepository)
}
@Test
fun `loadUser should update user LiveData`() = runBlockingTest {
// 给定
val user = User(1, "Alice")
coEvery { userRepository.getUser(1) } returns user
// 当
viewModel.loadUser(1)
// 那么
val result = viewModel.user.getOrAwaitValue()
assertEquals(user, result)
}
}
3. 调试技巧
Kotlin
// 1. 使用debug标记
inline fun <T> debug(block: () -> T): T {
return if (BuildConfig.DEBUG) {
val start = System.currentTimeMillis()
val result = block()
val end = System.currentTimeMillis()
println("Debug: Execution time = ${end - start}ms")
result
} else {
block()
}
}
// 使用
val result = debug {
// 需要调试的代码
expensiveComputation()
}
// 2. 使用also进行调试
data.process()
.also { println("第一步结果: $it") }
.transform()
.also { println("转换后结果: $it") }
.filter { it.isValid() }
.also { println("最终结果: $it") }
// 3. 条件断点
fun complexFunction(data: List<Int>) {
data.forEach { item ->
// 右键点击行号 -> 条件断点
// 条件: item > 100 && item < 200
processItem(item)
}
}
// 4. 日志工具类
object Logger {
private const val TAG = "MyApp"
inline fun <reified T> T.log(level: LogLevel = LogLevel.DEBUG, message: () -> String) {
if (shouldLog(level)) {
val tag = if (T::class == Logger::class) TAG else T::class.simpleName
println("[$tag] ${level.name}: ${message()}")
}
}
enum class LogLevel { DEBUG, INFO, WARN, ERROR }
private fun shouldLog(level: LogLevel): Boolean {
return BuildConfig.DEBUG || level >= LogLevel.WARN
}
}
// 使用
class UserService {
fun getUser(id: Int): User? {
Logger.log { "Getting user with id: $id" }
// ...
}
}
三十四、Kotlin工具链与构建
1. Gradle配置
bash
// build.gradle.kts
plugins {
kotlin("jvm") version "1.8.0"
kotlin("plugin.spring") version "1.8.0"
kotlin("kapt") version "1.8.0"
}
dependencies {
// Kotlin标准库
implementation(kotlin("stdlib"))
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("stdlib-jdk7"))
implementation(kotlin("reflect"))
// 协程
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
// 序列化
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
// 测试
testImplementation(kotlin("test"))
testImplementation(kotlin("test-junit"))
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
// Mocking
testImplementation("io.mockk:mockk:1.13.4")
// Ktor (Web框架)
implementation("io.ktor:ktor-server-core:2.2.3")
implementation("io.ktor:ktor-server-netty:2.2.3")
}
tasks {
compileKotlin {
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs = listOf("-Xjsr305=strict")
allWarningsAsErrors = true
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs = listOf("-Xjsr305=strict")
}
}
}
// 配置Kapt(注解处理)
kapt {
correctErrorTypes = true
useBuildCache = true
}
2. KSP(Kotlin Symbol Processing)
Kotlin
// 1. 创建KSP处理器
// build.gradle.kts
plugins {
kotlin("jvm")
id("com.google.devtools.ksp") version "1.8.0-1.0.9"
}
dependencies {
implementation("com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.9")
ksp("com.example:my-processor:1.0.0")
}
// 2. 定义注解
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class JsonSerializable
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
annotation class JsonName(val name: String)
// 3. 实现KSP处理器
class JsonProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger
) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver
.getSymbolsWithAnnotation(JsonSerializable::class.qualifiedName!!)
.filterIsInstance<KSClassDeclaration>()
symbols.forEach { classDeclaration ->
generateJsonAdapter(classDeclaration)
}
return emptyList()
}
private fun generateJsonAdapter(classDeclaration: KSClassDeclaration) {
val packageName = classDeclaration.packageName.asString()
val className = "${classDeclaration.simpleName.asString()}JsonAdapter"
val file = codeGenerator.createNewFile(
dependencies = Dependencies(true),
packageName = packageName,
fileName = className
)
file.writeText("""
|package $packageName
|
|import kotlinx.serialization.*
|import kotlinx.serialization.json.*
|
|class ${className} {
| fun toJson(obj: ${classDeclaration.simpleName.asString()}): String {
| return Json.encodeToString(obj)
| }
|
| fun fromJson(json: String): ${classDeclaration.simpleName.asString()} {
| return Json.decodeFromString(json)
| }
|}
""".trimMargin())
file.close()
}
}
// 4. 使用生成的代码
@JsonSerializable
data class User(
@JsonName("user_name")
val name: String,
val age: Int,
val email: String
)
// KSP会自动生成 UserJsonAdapter 类
3. 编译器插件
Kotlin
// 1. 使用无符号类型
val uint: UInt = 42u
val ulong: ULong = 1000uL
val ushort: UShort = 65535u
val ubyte: UByte = 255u
// 2. 使用内联类编译器插件
// build.gradle.kts
kotlin {
sourceSets.all {
languageSettings {
optIn("kotlin.ExperimentalUnsignedTypes")
optIn("kotlin.ExperimentalStdlibApi")
optIn("kotlin.time.ExperimentalTime")
}
}
}
// 3. 使用编译器参数优化
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
// 启用内联类
freeCompilerArgs += "-Xinline-classes"
// 启用新的类型推断
freeCompilerArgs += "-Xnew-inference"
// 启用协程调试
freeCompilerArgs += "-Xcoroutines=debug"
// 启用实验性功能
freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
// 启用JVM默认方法
freeCompilerArgs += "-Xjvm-default=all"
}
}
三十五、Kotlin迁移策略
1. 渐进式迁移策略
Kotlin
// 策略1:新文件用Kotlin,旧文件逐步迁移
// Java文件
public class UserService {
public User getUser(int id) {
// Java代码
return userRepository.findById(id);
}
}
// Kotlin新文件
class ProductService(
private val productRepository: ProductRepository
) {
fun getProduct(id: Int): Product? {
return productRepository.findById(id)
}
}
// 策略2:在Java中调用Kotlin代码
// Kotlin文件
@file:JvmName("StringUtils") // 指定生成的Java类名
fun capitalize(str: String): String {
return str.replaceFirstChar { it.uppercase() }
}
// Java中调用
String result = StringUtils.capitalize("hello");
// 策略3:使用混合项目结构
// src/main/java/ - 存放Java代码
// src/main/kotlin/ - 存放Kotlin代码
// src/test/kotlin/ - 测试代码用Kotlin编写
2. 自动转换工具
Kotlin
// Android Studio/IntelliJ IDEA 提供自动转换
// 1. 打开Java文件
// 2. 选择 Code -> Convert Java File to Kotlin File
// 3. 手动修复转换后的问题
// 转换示例
// Java代码
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
// 转换后的Kotlin代码
class User(val name: String, val age: Int) {
override fun toString(): String {
return "User{name='$name', age=$age}"
}
}
// 进一步优化为数据类
data class User(val name: String, val age: Int)
3. 常见迁移问题与解决方案
Kotlin
// 问题1:静态成员迁移
// Java静态工具类
public class StringUtils {
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
}
// Kotlin解决方案1:顶层函数
// StringUtils.kt
fun isEmpty(str: String?): Boolean {
return str.isNullOrBlank()
}
// Kotlin解决方案2:伴生对象
class StringUtils {
companion object {
@JvmStatic
fun isEmpty(str: String?): Boolean {
return str.isNullOrBlank()
}
}
}
// 问题2:Builder模式迁移
// Java Builder模式
public class User {
private final String name;
private final int age;
private User(Builder builder) {
this.name = builder.name;
this.age = builder.age;
}
public static class Builder {
private String name;
private int age;
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public User build() {
return new User(this);
}
}
}
// Kotlin解决方案:默认参数 + apply
data class User(
val name: String,
val age: Int
) {
companion object {
fun create(block: Builder.() -> Unit): User {
return Builder().apply(block).build()
}
}
class Builder {
var name: String = ""
var age: Int = 0
fun build(): User = User(name, age)
}
}
// 使用
val user = User.create {
name = "Alice"
age = 25
}
// 问题3:单例模式迁移
// Java单例
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
// Kotlin解决方案:object关键字
object Singleton {
fun doSomething() {
println("Singleton operation")
}
}
// 问题4:工具类迁移
// Java工具类
public final class CollectionUtils {
private CollectionUtils() {}
public static <T> boolean isEmpty(Collection<T> collection) {
return collection == null || collection.isEmpty();
}
}
// Kotlin解决方案:扩展函数
fun <T> Collection<T>?.isEmpty(): Boolean {
return this == null || this.isEmpty()
}
// 使用
val list: List<String>? = null
if (list.isEmpty()) {
println("List is empty")
}
4. 测试迁移
三十六、Kotlin特性总结与Java对比
1. 核心特性对比表
|-----------------|----------------------------------------------|-----------------------------------------|-------------|
| 特性 | Java | Kotlin | 优势 |
| 空安全 | @Nullable / @NotNull 注解 | 类型系统内置 (String? / String) | 编译时检查,减少NPE |
| 类型推断 | var (Java 10+) 有限支持 | 全面的类型推断 (val x = 10) | 代码更简洁 |
| 数据类 | 需要手动编写getter/setter/equals等 | data class User(val name: String) | 自动生成样板代码 |
| 扩展函数 | 静态工具类 | fun String.capitalize(): String | 增强现有类,无需继承 |
| 默认参数 | 方法重载 | fun greet(name: String = "Guest") | 减少重载方法数量 |
| 命名参数 | 不支持 | connect(host="localhost", port=8080) | 提高可读性 |
| 字符串模板 | "Hello, " + name + "!" | "Hello, $name!" | 更简洁易读 |
| when表达式 | switch语句 | when (x) { ... } | 更强大,支持任意类型 |
| 属性 | 字段+getter/setter | var name: String | 统一访问方式 |
| 委托 | 需要手动实现委托模式 | by关键字 | 简化委托模式实现 |
| 协程 | Thread / ExecutorService / CompletableFuture | suspend函数 + 协程 | 简化异步编程 |
| 密封类 | 枚举+类层次 | sealed class Result | 类型安全的代数数据类型 |
| 内联类 | 包装类(有开销) | value class Password(val value: String) | 类型安全,无运行时开销 |
2. 性能对比
Kotlin
// Kotlin性能优化点:
// 1. 内联函数消除lambda开销
inline fun <T> measureTime(block: () -> T): T {
val start = System.currentTimeMillis()
val result = block()
val end = System.currentTimeMillis()
println("Time: ${end - start}ms")
return result
}
// 2. 使用基本类型数组
val intArray = IntArray(1000000) // 基本类型,无装箱
val integerList = List(1000000) { it } // 装箱类型
// 3. 使用序列进行惰性求值
val result = (1..1000000)
.asSequence() // 转换为序列
.filter { it % 2 == 0 } // 惰性操作
.map { it * 2 } // 惰性操作
.take(1000) // 只取前1000个
.toList() // 最终计算
// 4. 使用局部函数避免重复计算
fun processData(data: List<Int>): List<String> {
// 局部函数,可以访问外部变量
fun isValid(value: Int): Boolean {
return value > 0 && value < 100
}
return data.filter(::isValid).map { it.toString() }
}
3. 互操作最佳实践
Kotlin
// 1. 为Java调用优化Kotlin代码
class KotlinClass {
companion object {
@JvmStatic // 生成静态方法
fun staticMethod() { }
@JvmField // 生成公共字段
val CONSTANT = "value"
}
@JvmOverloads // 生成重载方法
fun method(param: String = "default") { }
}
// 2. 处理Java平台类型
fun processJavaList(list: List<String>) { // List<String>! 平台类型
// 安全处理
val safeList: List<String> = list ?: emptyList()
// 或使用扩展函数
list.orEmpty().forEach { println(it) }
}
// 3. 使用@JvmName解决签名冲突
@JvmName("filterStrings")
fun List<String>.filter(predicate: (String) -> Boolean): List<String> {
return this.filter(predicate)
}
@JvmName("filterInts")
fun List<Int>.filter(predicate: (Int) -> Boolean): List<Int> {
return this.filter(predicate)
}
// 4. 异常处理互操作
@Throws(IOException::class) // 为Java声明受检异常
fun readFile(path: String): String {
return File(path).readText()
}
三十七、学习资源与进阶路径
1. 官方资源
- 官方文档 : kotlinlang.org/docs
- Kotlin Playground : play.kotlinlang.org
- Kotlin Koans : kotlinlang.org/koans
- 官方博客 : blog.jetbrains.com/kotlin
2. 书籍推荐
- 《Kotlin in Action》 - Dmitry Jemerov & Svetlana Isakova
- 《Kotlin Programming: The Big Nerd Ranch Guide》 - Josh Skeen & David Greenhalgh
- 《Head First Kotlin》 - Dawn Griffiths & David Griffiths
- 《Effective Kotlin》 - Marcin Moskała
3. 在线课程
- Kotlin Bootcamp for Programmers (Google)
- Kotlin for Java Developers (JetBrains on Coursera)
- Android Kotlin Fundamentals (Google)
4. 社区资源
- Kotlin Slack : kotlinlang.slack.com
- Reddit : r/kotlin
- Stack Overflow : kotlin标签
- GitHub : kotlin/kotlin 仓库
5. 工具与框架
bash
// 常用框架
dependencies {
// Web框架
implementation("io.ktor:ktor-server-core:2.3.0")
implementation("io.ktor:ktor-server-netty:2.3.0")
// HTTP客户端
implementation("io.ktor:ktor-client-core:2.3.0")
implementation("io.ktor:ktor-client-okhttp:2.3.0")
// 序列化
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.0")
// 数据库
implementation("org.jetbrains.exposed:exposed-core:0.40.1")
implementation("org.jetbrains.exposed:exposed-dao:0.40.1")
implementation("org.jetbrains.exposed:exposed-jdbc:0.40.1")
// 依赖注入
implementation("org.koin:koin-core:3.3.3")
// 测试
testImplementation("io.kotest:kotest-runner-junit5:5.5.5")
testImplementation("io.mockk:mockk:1.13.4")
}
// 开发工具
// 1. IntelliJ IDEA / Android Studio
// 2. Kotlin Plugin
// 3. ktlint (代码风格检查)
// 4. detekt (静态代码分析)
// 5. gradle-kotlin-dsl (Gradle Kotlin DSL)
三十八、实战项目结构示例
// 典型的Kotlin多模块项目结构
project/
├── build.gradle.kts
├── settings.gradle.kts
├── buildSrc/
│ └── src/main/kotlin/Dependencies.kt
├── core/
│ └── src/main/kotlin/com/example/core/
├── feature-auth/
│ └── src/main/kotlin/com/example/auth/
├── feature-user/ # 用户功能模块
│ ├── build.gradle.kts
│ └── src/main/kotlin/com/example/user/
│ ├── di/
│ ├── domain/
│ ├── data/
│ └── presentation/
├── app/ # 应用模块
│ ├── build.gradle.kts
│ └── src/main/kotlin/com/example/app/
│ ├── di/
│ ├── Main.kt # 应用入口
│ └── config/
└── shared/ # 共享模块
├── build.gradle.kts
└── src/commonMain/kotlin/ # 多平台共享代码
三十九、代码规范与最佳实践
1. 命名规范
Kotlin
// 包名:全小写,不使用下划线
package com.example.myproject
// 类/接口:大驼峰
class UserRepository
interface DataSource
// 函数/变量:小驼峰
fun calculateTotalPrice()
val userCount: Int
// 常量:大写+下划线
const val MAX_RETRY_COUNT = 3
const val API_BASE_URL = "https://api.example.com"
// 测试函数:使用反引号
@Test
fun `should return user when valid id provided`() {
// 测试代码
}
// 扩展函数:明确的前缀
fun String.toSlug(): String
fun View.show()
fun View.hide()
// 工厂函数:使用伴生对象
class User private constructor(val id: String) {
companion object {
fun create(id: String): User {
return User(id)
}
}
}
2. 代码风格
Kotlin
// 1. 使用表达式体
fun isEven(number: Int): Boolean = number % 2 == 0
// 2. 使用默认参数
fun connect(
host: String = "localhost",
port: Int = 8080,
timeout: Int = 5000
) { /* ... */ }
// 3. 使用命名参数
connect(
host = "api.example.com",
port = 443,
timeout = 10000
)
// 4. 使用数据类
data class User(
val id: Long,
val name: String,
val email: String,
val createdAt: Instant = Instant.now()
)
// 5. 使用密封类处理状态
sealed class ApiState<out T> {
object Loading : ApiState<Nothing>()
data class Success<T>(val data: T) : ApiState<T>()
data class Error(val message: String) : ApiState<Nothing>()
}
// 6. 使用when表达式
fun describeNumber(n: Int): String = when {
n < 0 -> "负数"
n == 0 -> "零"
n in 1..10 -> "小的正数"
else -> "大的正数"
}
// 7. 使用扩展函数
fun String.isValidEmail(): Boolean {
return matches(Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}$"))
}
// 8. 使用作用域函数
val user = User(
id = 1,
name = "Alice",
email = "alice@example.com"
).also {
logger.info("User created: $it")
}
3. 错误处理模式
Kotlin
// 1. 使用Result类型
fun parseNumber(str: String): Result<Int> = runCatching {
str.toInt()
}
// 使用
parseNumber("123").fold(
onSuccess = { println("Parsed: $it") },
onFailure = { println("Failed: ${it.message}") }
)
// 2. 使用Either类型
sealed class Either<out L, out R> {
data class Left<out L>(val value: L) : Either<L, Nothing>()
data class Right<out R>(val value: R) : Either<Nothing, R>()
}
fun divide(a: Int, b: Int): Either<String, Int> {
return if (b == 0) {
Either.Left("除数不能为零")
} else {
Either.Right(a / b)
}
}
// 3. 使用验证器模式
data class ValidationResult(
val isValid: Boolean,
val errors: List<String> = emptyList()
)
class Validator<T> {
private val validations = mutableListOf<(T) -> ValidationResult>()
fun addValidation(validation: (T) -> ValidationResult) {
validations.add(validation)
}
fun validate(value: T): ValidationResult {
val errors = validations
.map { it(value) }
.filter { !it.isValid }
.flatMap { it.errors }
return ValidationResult(errors.isEmpty(), errors)
}
}
// 使用
val userValidator = Validator<User>().apply {
addValidation { user ->
if (user.name.isBlank()) {
ValidationResult(false, listOf("姓名不能为空"))
} else {
ValidationResult(true)
}
}
addValidation { user ->
if (!user.email.isValidEmail()) {
ValidationResult(false, listOf("邮箱格式不正确"))
} else {
ValidationResult(true)
}
}
}
四十、常见陷阱与解决方案
1. 空安全陷阱
Kotlin
// 陷阱1:使用!!非空断言
val length: Int = nullableString!!.length // 可能抛出NPE
// 解决方案:使用安全调用或默认值
val length1: Int? = nullableString?.length // 安全调用
val length2: Int = nullableString?.length ?: 0 // Elvis操作符
// 陷阱2:平台类型
val javaList: List<String> = JavaClass.getList() // List<String>! 平台类型
// 解决方案:明确类型
val kotlinList:
Kotlin
// 陷阱2:平台类型
val javaList: List<String> = JavaClass.getList() // List<String>! 平台类型
// 解决方案:明确类型
val kotlinList: List<String> = JavaClass.getList() ?: emptyList()
val safeList = JavaClass.getList().orEmpty()
// 陷阱3:lateinit var 未初始化
class UserService {
private lateinit var repository: UserRepository
fun initialize() {
// 忘记初始化 repository
}
fun getUser(id: Int): User? {
return repository.findById(id) // 运行时抛出异常:lateinit property repository has not been initialized
}
}
// 解决方案1:使用可为空类型
class UserService {
private var repository: UserRepository? = null
fun initialize(repo: UserRepository) {
repository = repo
}
fun getUser(id: Int): User? {
return repository?.findById(id)
}
}
// 解决方案2:使用依赖注入
class UserService(private val repository: UserRepository) {
fun getUser(id: Int): User? {
return repository.findById(id)
}
}
// 解决方案3:使用 lazy 初始化
class UserService {
private val repository by lazy {
UserRepository()
}
fun getUser(id: Int): User? {
return repository.findById(id)
}
}
2. 集合陷阱
Kotlin
// 陷阱1:误用可变集合
val list = listOf(1, 2, 3) // 不可变列表
list.add(4) // 编译错误
val mutableList = mutableListOf(1, 2, 3) // 可变列表
mutableList.add(4) // 正确
// 陷阱2:集合拷贝
val original = mutableListOf(1, 2, 3)
val copy = original // 这只是引用拷贝,不是内容拷贝
copy.add(4)
println(original) // [1, 2, 3, 4] 原集合也被修改了
// 解决方案:使用 toList() 或 toMutableList()
val safeCopy = original.toList() // 创建新列表
// 或
val mutableCopy = original.toMutableList()
// 陷阱3:集合操作性能
val largeList = (1..1_000_000).toList()
// 低效:多次中间集合
val result1 = largeList
.filter { it % 2 == 0 } // 创建中间集合
.map { it * 2 } // 创建中间集合
.take(1000) // 创建中间集合
.toList()
// 高效:使用序列
val result2 = largeList.asSequence()
.filter { it % 2 == 0 } // 惰性操作
.map { it * 2 } // 惰性操作
.take(1000) // 惰性操作
.toList() // 最终计算
// 陷阱4:集合与数组转换
val list = listOf(1, 2, 3)
val array: Array<Int> = list.toTypedArray() // 装箱的 Integer 数组
val intArray: IntArray = list.toIntArray() // 基本类型 int 数组(更高效)
3. 类型系统陷阱
Kotlin
// 陷阱1:类型擦除
fun <T> checkType(obj: Any): Boolean {
return obj is T // 编译警告:Cannot check for instance of erased type: T
}
// 解决方案:使用 reified 和内联函数
inline fun <reified T> checkTypeReified(obj: Any): Boolean {
return obj is T // 正确
}
// 陷阱2:协变和逆变
open class Animal
class Dog : Animal()
class Cat : Animal()
// 协变:只能读取,不能写入
val animals: List<Animal> = listOf(Dog(), Cat()) // 正确
val dogs: List<Dog> = listOf(Dog())
// animals = dogs // 编译错误
// 逆变:只能写入,不能读取
interface Consumer<in T> {
fun consume(item: T)
}
val animalConsumer: Consumer<Animal> = object : Consumer<Animal> {
override fun consume(item: Animal) { /* ... */ }
}
val dogConsumer: Consumer<Dog> = animalConsumer // 正确,逆变
// dogConsumer.consume(Dog()) // 实际上调用的是 animalConsumer.consume(Dog())
// 陷阱3:星投影
fun printList(list: List<*>) {
// list 的元素类型是未知的
for (item in list) {
println(item) // item 的类型是 Any?
}
// list.add(anything) // 编译错误:不能添加元素
}
// 正确使用
fun <T> processList(list: List<T>, processor: (T) -> Unit) {
for (item in list) {
processor(item)
}
}
4. 函数式编程陷阱
Kotlin
// 陷阱1:过度使用链式调用
val result = list
.filter { it > 0 }
.map { it * 2 }
.sorted()
.distinct()
.take(10)
.joinToString()
// 解决方案:考虑可读性和性能
val result = list
.asSequence() // 对于大数据集使用序列
.filter { it > 0 }
.map { it * 2 }
.sorted()
.distinct()
.take(10)
.joinToString()
// 陷阱2:误用 let/also/apply/run
val user = User().apply {
name = "Alice"
age = 25
}.also {
println("Created user: $it")
}.let {
it.copy(email = "${it.name.lowercase()}@example.com")
}
// 选择合适的作用域函数:
// - apply: 对象配置,返回对象本身
// - also: 附加操作,返回对象本身
// - let: 转换对象,返回lambda结果
// - run: 对象配置和计算,返回lambda结果
// - with: 在对象上执行操作,返回lambda结果
// 陷阱3:高阶函数性能
// 内联函数适合小型lambda,非内联函数适合大型lambda
inline fun <T> processInline(items: List<T>, processor: (T) -> Unit) {
// 适合:processor很小,内联可以提升性能
for (item in items) {
processor(item)
}
}
fun <T> processNoInline(items: List<T>, processor: (T) -> Unit) {
// 适合:processor很大,避免代码膨胀
for (item in items) {
processor(item)
}
}
5. 协程陷阱
Kotlin
// 陷阱1:忘记处理取消
suspend fun longRunningTask() {
repeat(1000) { i ->
// 忘记检查取消状态
delay(100)
println("Processing $i")
}
}
// 解决方案:使用 ensureActive 或 yield
suspend fun longRunningTaskFixed() = coroutineScope {
repeat(1000) { i ->
ensureActive() // 检查是否被取消
// 或 yield()
delay(100)
println("Processing $i")
}
}
// 陷阱2:阻塞主线程
fun loadData() {
// 错误:在UI线程启动长时间运行的任务
GlobalScope.launch(Dispatchers.Main) {
val data = fetchData() // 挂起函数,但可能阻塞
updateUI(data)
}
}
// 解决方案:使用适当的调度器
fun loadDataCorrect() {
viewModelScope.launch {
// 在IO线程执行网络请求
val data = withContext(Dispatchers.IO) {
fetchData()
}
// 在主线程更新UI
withContext(Dispatchers.Main) {
updateUI(data)
}
}
}
// 陷阱3:异常处理不当
fun riskyOperation() {
GlobalScope.launch {
try {
throw RuntimeException("Error!")
} catch (e: Exception) {
// 异常被捕获,但协程仍然崩溃
}
}
}
// 解决方案:使用 CoroutineExceptionHandler 或 SupervisorJob
fun safeOperation() {
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
val scope = CoroutineScope(SupervisorJob() + exceptionHandler)
scope.launch {
throw RuntimeException("Error!") // 被exceptionHandler捕获
}
scope.launch {
delay(1000)
println("This still runs") // 仍然执行
}
}
// 陷阱4:内存泄漏
class MyActivity : AppCompatActivity() {
private val scope = CoroutineScope(Dispatchers.Main)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
scope.launch {
// 长时间运行的任务
delay(5000)
updateUI() // Activity可能已经被销毁!
}
}
override fun onDestroy() {
super.onDestroy()
scope.cancel() // 必须手动取消!
}
}
// 解决方案1:使用lifecycleScope(Android)
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
// 自动跟随生命周期
delay(5000)
if (isActive) { // 检查是否仍然活跃
updateUI()
}
}
}
}
// 解决方案2:使用viewModelScope(ViewModel)
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
val data = repository.fetchData()
_data.value = data
}
}
}
// 解决方案3:结构化并发
suspend fun fetchUserData(userId: String): UserData = coroutineScope {
val userDeferred = async { api.getUser(userId) }
val postsDeferred = async { api.getPosts(userId) }
try {
UserData(
user = userDeferred.await(),
posts = postsDeferred.await()
)
} catch (e: Exception) {
// 如果任何一个子协程失败,所有子协程都会被取消
throw e
}
}
// 陷阱5:并发修改共享状态
class Counter {
var count = 0
suspend fun increment() {
// 危险:多个协程可能同时修改
count++
}
}
// 解决方案1:使用Mutex
class SafeCounter {
private var count = 0
private val mutex = Mutex()
suspend fun increment() {
mutex.withLock {
count++
}
}
suspend fun getCount(): Int {
return mutex.withLock { count }
}
}
// 解决方案2:使用Actor
sealed class CounterMessage
object Increment : CounterMessage()
class GetCount(val response: CompletableDeferred<Int>) : CounterMessage()
fun CoroutineScope.counterActor() = actor<CounterMessage> {
var count = 0
for (msg in channel) {
when (msg) {
is Increment -> count++
is GetCount -> msg.response.complete(count)
}
}
}
// 使用
val counter = counterActor()
suspend fun testActor() {
repeat(1000) {
launch {
counter.send(Increment)
}
}
delay(1000)
val response = CompletableDeferred<Int>()
counter.send(GetCount(response))
println("Count: ${response.await()}")
counter.close()
}
四十一、Android开发特有转换
1. Activity和Fragment
Kotlin
// Java Activity
public class MainActivity extends AppCompatActivity {
private TextView textView;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text_view);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView.setText("Button Clicked!");
}
});
}
}
// Kotlin Activity
class MainActivity : AppCompatActivity() {
// 使用视图绑定或DataBinding
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.setOnClickListener {
binding.textView.text = "Button Clicked!"
}
}
}
// 使用Kotlin扩展简化
fun AppCompatActivity.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
fun View.onClick(action: () -> Unit) {
setOnClickListener { action() }
}
// 简化后的Activity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.onClick {
binding.textView.text = "Button Clicked!"
toast("Button clicked!")
}
}
}
2. ViewModel和LiveData
Kotlin
// Java ViewModel
public class UserViewModel extends ViewModel {
private MutableLiveData<User> user = new MutableLiveData<>();
private UserRepository repository = new UserRepository();
public LiveData<User> getUser() {
return user;
}
public void loadUser(int userId) {
repository.getUser(userId, new Callback<User>() {
@Override
public void onSuccess(User result) {
user.setValue(result);
}
@Override
public void onError(Exception e) {
// 处理错误
}
});
}
}
// Kotlin ViewModel
class UserViewModel : ViewModel() {
private val repository = UserRepository()
private val _user = MutableLiveData<User>()
val user: LiveData<User> = _user
private val _loading = MutableLiveData<Boolean>()
val loading: LiveData<Boolean> = _loading
private val _error = MutableLiveData<String>()
val error: LiveData<String> = _error
fun loadUser(userId: Int) {
viewModelScope.launch {
_loading.value = true
try {
val result = repository.getUser(userId)
_user.value = result
} catch (e: Exception) {
_error.value = e.message
} finally {
_loading.value = false
}
}
}
}
// 使用StateFlow(替代LiveData)
class UserViewModel : ViewModel() {
private val repository = UserRepository()
private val _userState = MutableStateFlow<UserState>(UserState.Loading)
val userState: StateFlow<UserState> = _userState
fun loadUser(userId: Int) {
viewModelScope.launch {
_userState.value = UserState.Loading
try {
val user = repository.getUser(userId)
_userState.value = UserState.Success(user)
} catch (e: Exception) {
_userState.value = UserState.Error(e.message ?: "Unknown error")
}
}
}
}
sealed class UserState {
object Loading : UserState()
data class Success(val user: User) : UserState()
data class Error(val message: String) : UserState()
}
3. RecyclerView适配器
Kotlin
// Java适配器
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.ViewHolder> {
private List<User> users = new ArrayList<>();
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_user, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
User user = users.get(position);
holder.nameTextView.setText(user.getName());
holder.emailTextView.setText(user.getEmail());
}
@Override
public int getItemCount() {
return users.size();
}
public void setUsers(List<User> users) {
this.users = users;
notifyDataSetChanged();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView nameTextView;
TextView emailTextView;
ViewHolder(View itemView) {
super(itemView);
nameTextView = itemView.findViewById(R.id.name_text_view);
emailTextView = itemView.findViewById(R.id.email_text_view);
}
}
}
// Kotlin适配器(使用ViewBinding)
class UserAdapter : RecyclerView.Adapter<UserAdapter.ViewHolder>() {
private var users: List<User> = emptyList()
var onItemClick: ((User) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val user = users[position]
holder.bind(user)
holder.itemView.setOnClickListener {
onItemClick?.invoke(user)
}
}
override fun getItemCount(): Int = users.size
fun submitList(newUsers: List<User>) {
val diffResult = DiffUtil.calculateDiff(UserDiffCallback(users, newUsers))
users = newUsers
diffResult.dispatchUpdatesTo(this)
}
class ViewHolder(
private val binding: ItemUserBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(user: User) {
binding.nameTextView.text = user.name
binding.emailTextView.text = user.email
binding.ageTextView.text = user.age.toString()
// 使用扩展函数加载图片
binding.avatarImageView.load(user.avatarUrl) {
placeholder(R.drawable.placeholder)
error(R.drawable.error)
}
}
}
class UserDiffCallback(
private val oldList: List<User>,
private val newList: List<User>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
}
// 使用ListAdapter简化
class UserListAdapter : ListAdapter<User, UserListAdapter.ViewHolder>(UserDiffCallback()) {
class ViewHolder(
private val binding: ItemUserBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(user: User) {
binding.user = user
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
class UserDiffCallback : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem == newItem
}
}
}
4. 网络请求(Retrofit + Coroutines)
Kotlin
// Java Retrofit接口
public interface ApiService {
@GET("users/{id}")
Call<User> getUser(@Path("id") int userId);
@POST("users")
Call<User> createUser(@Body User user);
}
// Kotlin Retrofit接口(使用协程)
interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") userId: Int): User
@POST("users")
suspend fun createUser(@Body user: User): User
@GET("users")
suspend fun getUsers(@Query("page") page: Int): List<User>
@Multipart
@POST("upload")
suspend fun uploadFile(@Part file: MultipartBody.Part): UploadResponse
}
// 使用Kotlin扩展简化Retrofit
inline fun <reified T> createApiService(baseUrl: String): T {
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(T::class.java)
}
// 或者使用Ktor客户端(纯Kotlin)
val httpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
})
}
install(Logging) {
level = LogLevel.HEADERS
}
}
suspend fun fetchUser(userId: Int): User {
return httpClient.get("https://api.example.com/users/$userId")
}
// Repository模式
class UserRepository(
private val apiService: ApiService,
private val database: UserDatabase
) {
private val cache = mutableMapOf<Int, User>()
suspend fun getUser(userId: Int): User {
// 先从缓存获取
cache[userId]?.let { return it }
// 然后从数据库获取
val localUser = database.userDao().getById(userId)
if (localUser != null) {
cache[userId] = localUser
return localUser
}
// 最后从网络获取
return try {
val remoteUser = apiService.getUser(userId)
database.userDao().insert(remoteUser)
cache[userId] = remoteUser
remoteUser
} catch (e: Exception) {
throw UserNotFoundException("User not found: $userId", e)
}
}
suspend fun getUsers(page: Int): List<User> {
return try {
val users = apiService.getUsers(page)
database.userDao().insertAll(users)
users
} catch (e: Exception) {
// 网络失败时返回本地数据
database.userDao().getAll()
}
}
}
5. 数据库(Room + Coroutines)
Kotlin
// Java Room实体
@Entity(tableName = "users")
public class User {
@PrimaryKey
public int id;
@ColumnInfo(name = "user_name")
public String name;
public String email;
@Ignore
public String temporaryField;
}
// Kotlin Room实体
@Entity(tableName = "users")
data class User(
@PrimaryKey
val id: Int,
@ColumnInfo(name = "user_name")
val name: String,
val email: String,
@ColumnInfo(defaultValue = "CURRENT_TIMESTAMP")
val createdAt: Date = Date()
) {
// 忽略的字段
@Ignore
var temporaryField: String? = null
}
// DAO接口
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(user: User)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(users: List<User>)
@Update
suspend fun update(user: User)
@Delete
suspend fun delete(user: User)
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getById(userId: Int): User?
@Query("SELECT * FROM users ORDER BY name ASC")
fun getAll(): Flow<List<User>>
@Query("SELECT * FROM users WHERE name LIKE :query")
suspend fun search(query: String): List<User>
@Transaction
suspend fun updateWithLog(user: User) {
update(user)
// 可以执行多个操作
}
}
// 数据库类
@Database(
entities = [User::class, Post::class],
version = 1,
exportSchema = false
)
@TypeConverters(DateConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun postDao(): PostDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database"
)
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
// 数据库创建时的操作
}
})
.build()
INSTANCE = instance
instance
}
}
}
}
// 类型转换器
class DateConverter {
@TypeConverter
fun fromTimestamp(value: Long?): Date? {
return value?.let { Date(it) }
}
@TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time
}
}
四十二、 Android开发中的Kotlin实践指南
1. build.gradle配置
Kotlin
// 项目级 build.gradle.kts
plugins {
id("com.android.application") version "8.0.0" apply false
id("com.android.library") version "8.0.0" apply false
id("org.jetbrains.kotlin.android") version "1.8.0" apply false
id("com.google.dagger.hilt.android") version "2.44" apply false
}
// 模块级 build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
id("dagger.hilt.android.plugin")
id("androidx.navigation.safeargs.kotlin")
}
android {
namespace = "com.example.myapp"
compileSdk = 33
defaultConfig {
applicationId = "com.example.myapp"
minSdk = 24
targetSdk = 33
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
getByName("debug") {
applicationIdSuffix = ".debug"
isDebuggable = true
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs = listOf(
"-Xopt-in=kotlin.RequiresOptIn",
"-Xjvm-default=all"
)
}
buildFeatures {
viewBinding = true
dataBinding = true
compose = true // 如果使用Compose
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.0"
}
}
dependencies {
// Kotlin
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
// AndroidX Core
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.activity:activity-ktx:1.6.1")
implementation("androidx.fragment:fragment-ktx:1.5.5")
// UI
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.recyclerview:recyclerview:1.3.0")
implementation("androidx.viewpager2:viewpager2:1.0.0")
// Lifecycle
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
implementation("androidx.lifecycle:lifecycle-common-java8:2.6.1")
// Navigation
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
// Room
implementation("androidx.room:room-runtime:2.5.0")
implementation("androidx.room:room-ktx:2.5.0")
kapt("androidx.room:room-compiler:2.5.0")
// Hilt
implementation("com.google.dagger:hilt-android:2.44")
kapt("com.google.dagger:hilt-compiler:2.44")
// Networking
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
// Image Loading
implementation("io.coil-kt:coil:2.2.2")
// Timber (Logging)
implementation("com.jakewharton.timber:timber:5.0.1")
// Testing
testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
testImplementation("io.mockk:mockk:1.13.4")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
二、常用扩展函数
Kotlin
// ViewExtensions.kt
import android.view.View
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
fun View.invisible() {
visibility = View.INVISIBLE
}
fun View.isVisible(): Boolean = visibility == View.VISIBLE
fun View.isGone(): Boolean = visibility == View.GONE
fun View.isInvisible(): Boolean = visibility == View.INVISIBLE
fun View.onClick(delay: Long = 200, action: () -> Unit) {
setOnClickListener {
it.isClickable = false
action()
it.postDelayed({ it.isClickable = true }, delay)
}
}
// ContextExtensions.kt
import android.content.Context
import android.widget.Toast
fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()
}
fun Context.showLongToast(message: String) {
toast(message, Toast.LENGTH_LONG)
}
fun Context.dpToPx(dp: Float): Int {
return (dp * resources.displayMetrics.density).toInt()
}
fun Context.pxToDp(px: Int): Float {
return px / resources.displayMetrics.density
}
// StringExtensions.kt
fun String?.orEmpty(): String = this ?: ""
fun String?.isNotNullOrEmpty(): Boolean = !this.isNullOrEmpty()
fun String?.isNotNullOrBlank(): Boolean = !this.isNullOrBlank()
fun String.capitalizeWords(): String {
return split(" ").joinToString(" ") { word ->
word.replaceFirstChar { it.uppercase() }
}
}
// ListExtensions.kt
fun <T> List<T>?.orEmpty(): List<T> = this ?: emptyList()
fun <T> List<T>.secondOrNull(): T? = getOrNull(1)
fun <T> List<T>.secondLastOrNull(): T? = getOrNull(size - 2)
// SharedPreferences扩展
import android.content.SharedPreferences
fun SharedPreferences.edit(block: SharedPreferences.Editor.() -> Unit) {
val editor = edit()
editor.block()
editor.apply()
}
inline fun <reified T> SharedPreferences.get(key: String, defaultValue: T): T {
return when (T::class) {
String::class -> getString(key, defaultValue as? String) as T
Int::class -> getInt(key, defaultValue as? Int ?: 0) as T
Long::class -> getLong(key, defaultValue as? Long ?: 0L) as T
Float::class -> getFloat(key, defaultValue as? Float ?: 0f) as T
Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T
else -> throw IllegalArgumentException("Unsupported type")
}
}
// Bundle扩展
import android.os.Bundle
fun Bundle.put(key: String, value: Any?) {
when (value) {
null -> putString(key, null)
is String -> putString(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Float -> putFloat(key, value)
is Boolean -> putBoolean(key, value)
is Bundle -> putBundle(key, value)
is Parcelable -> putParcelable(key, value)
is Serializable -> putSerializable(key, value)
else -> throw IllegalArgumentException("Unsupported type: ${value.javaClass}")
}
}
三、ViewModel与LiveData
Kotlin
// BaseViewModel.kt
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
abstract class BaseViewModel : ViewModel() {
protected val _loading = MutableStateFlow(false)
val loading: StateFlow<Boolean> = _loading
protected val _error = MutableStateFlow<String?>(null)
val error: StateFlow<String?> = _error
protected val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
viewModelScope.launch {
_error.value = throwable.message ?: "未知错误"
}
}
protected fun launchWithLoading(block: suspend () -> Unit) {
viewModelScope.launch(exceptionHandler) {
_loading.value = true
try {
block()
} finally {
_loading.value = false
}
}
}
fun clearError() {
_error.value = null
}
}
// UserViewModel.kt
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
class UserViewModel(
private val userRepository: UserRepository
) : BaseViewModel() {
private val _userState = MutableStateFlow<UserState>(UserState.Idle)
val userState: StateFlow<UserState> = _userState
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users
fun loadUser(userId: Int) {
launchWithLoading {
userRepository.getUser(userId)
.onStart { _userState.value = UserState.Loading }
.catch { error ->
_userState.value = UserState.Error(error.message ?: "加载失败")
}
.collect { user ->
_userState.value = UserState.Success(user)
}
}
}
fun loadUsers() {
viewModelScope.launch {
userRepository.getUsers()
.onStart { _loading.value = true }
.catch { error ->
_error.value = error.message
}
.collect { users ->
_users.value = users
}
}
}
fun searchUsers(query: String) {
viewModelScope.launch {
userRepository.searchUsers(query)
.debounce(300) // 防抖
.distinctUntilChanged()
.collect { users ->
_users.value = users
}
}
}
fun updateUser(user: User) {
launchWithLoading {
userRepository.updateUser(user)
// 更新本地状态
_users.update { list ->
list.map { if (it.id == user.id) user else it }
}
}
}
fun deleteUser(userId: Int) {
launchWithLoading {
userRepository.deleteUser(userId)
// 更新本地状态
_users.update { list ->
list.filter { it.id != userId }
}
}
}
}
// 状态封装
sealed class UserState {
object Idle : UserState()
object Loading : UserState()
data class Success(val user: User) : UserState()
data class Error(val message: String) : UserState()
}
// Repository实现
class UserRepositoryImpl(
private val apiService: ApiService,
private val userDao: UserDao
) : UserRepository {
override fun getUser(userId: Int): Flow<User> = flow {
// 先尝试从数据库获取
val cachedUser = userDao.getUserById(userId)
cachedUser?.let { emit(it) }
// 从网络获取
try {
val remoteUser = apiService.getUser(userId)
userDao.insert(remoteUser)
emit(remoteUser)
} catch (e: Exception) {
if (cachedUser == null) {
throw e // 没有缓存数据,抛出异常
}
// 有缓存数据,使用缓存
}
}
override fun getUsers(): Flow<List<User>> = flow {
// 监听数据库变化
userDao.getAllUsers().collect { cachedUsers ->
emit(cachedUsers)
}
// 同时从网络获取最新数据
try {
val remoteUsers = apiService.getUsers()
userDao.insertAll(remoteUsers)
} catch (e: Exception) {
// 网络失败,使用缓存数据
}
}
override suspend fun updateUser(user: User) {
try {
val updatedUser = apiService.updateUser(user.id, user)
userDao.insert(updatedUser)
} catch (e: Exception) {
// 先更新本地,稍后同步
userDao.insert(user.copy(isSynced = false))
throw e
}
}
}
四、Compose UI开发
Kotlin
// 1. 主题配置
@Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
// 2. 主界面
@Composable
fun MainScreen(
viewModel: MainViewModel = hiltViewModel()
) {
val userState by viewModel.userState.collectAsState()
val users by viewModel.users.collectAsState()
MyAppTheme {
Scaffold(
topBar = {
TopAppBar(
title = { Text("用户列表") },
actions = {
IconButton(onClick = { viewModel.refresh() }) {
Icon(Icons.Filled.Refresh, "刷新")
}
}
)
},
floatingActionButton = {
FloatingActionButton(onClick = { /* 添加用户 */ }) {
Icon(Icons.Filled.Add, "添加")
}
}
) { padding ->
when (val state = userState) {
is UserState.Loading -> LoadingScreen()
is UserState.Error -> ErrorScreen(state.message) {
viewModel.refresh()
}
is UserState.Success -> UserListScreen(
users = users,
onUserClick = { user ->
// 导航到用户详情
},
modifier = Modifier.padding(padding)
)
UserState.Idle -> {}
}
}
}
}
// 3. 用户列表
@Composable
fun UserListScreen(
users: List<User>,
onUserClick: (User) -> Unit,
modifier: Modifier = Modifier
) {
LazyColumn(modifier = modifier.fillMaxSize()) {
items(users, key = { it.id }) { user ->
UserItem(
user = user,
onClick = { onUserClick(user) }
)
}
}
}
@Composable
fun UserItem(
user: User,
onClick: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.clickable(onClick = onClick),
elevation = 4.dp
) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
AsyncImage(
model = user.avatarUrl,
contentDescription = "用户头像",
modifier = Modifier
.size(48.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.width(16.dp))
Column {
Text(
text = user.name,
style = MaterialTheme.typography.h6,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = user.email,
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
)
}
Spacer(modifier = Modifier.weight(1f))
Icon(
imageVector = Icons.Default.ArrowForward,
contentDescription = "查看详情",
tint = MaterialTheme.colors.primary
)
}
}
}
// 4. 加载和错误状态
@Composable
fun LoadingScreen() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
@Composable
fun ErrorScreen(
message: String,
onRetry: () -> Unit
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Icon(
imageVector = Icons.Filled.Error,
contentDescription = "错误",
tint = MaterialTheme.colors.error,
modifier = Modifier.size(64.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "出错了",
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.error
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = message,
style = MaterialTheme.typography.body2,
textAlign = TextAlign.Center,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
)
Spacer(modifier = Modifier.height(24.dp))
Button(
onClick = onRetry,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
)
) {
Text(text = "重试")
}
}
}
// 5. 表单输入
@Composable
fun LoginScreen(
viewModel: LoginViewModel = hiltViewModel()
) {
val email by viewModel.email.collectAsState()
val password by viewModel.password.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
val error by viewModel.error.collectAsState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "登录",
style = MaterialTheme.typography.h4,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(bottom = 32.dp)
)
OutlinedTextField(
value = email,
onValueChange = viewModel::onEmailChange,
label = { Text("邮箱") },
placeholder = { Text("请输入邮箱") },
leadingIcon = { Icon(Icons.Default.Email, null) },
isError = error?.contains("邮箱") == true,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = password,
onValueChange = viewModel::onPasswordChange,
label = { Text("密码") },
placeholder = { Text("请输入密码") },
leadingIcon = { Icon(Icons.Default.Lock, null) },
visualTransformation = PasswordVisualTransformation(),
isError = error?.contains("密码") == true,
modifier = Modifier.fillMaxWidth()
)
error?.let {
Text(
text = it,
color = MaterialTheme.colors.error,
style = MaterialTheme.typography.caption,
modifier = Modifier.padding(top = 8.dp)
)
}
Spacer(modifier = Modifier.height(24.dp))
Button(
onClick = viewModel::login,
enabled = !isLoading,
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
strokeWidth = 2.dp,
color = MaterialTheme.colors.onPrimary
)
} else {
Text(text = "登录")
}
}
Spacer(modifier = Modifier.height(16.dp))
TextButton(onClick = { /* 跳转到注册 */ }) {
Text(text = "没有账号?立即注册")
}
}
}
// 6. ViewModel for Compose
class LoginViewModel @Inject constructor(
private val authRepository: AuthRepository
) : ViewModel() {
private val _email = MutableStateFlow("")
val email: StateFlow<String> = _email
private val _password = MutableStateFlow("")
val password: StateFlow<String> = _password
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading
private val _error = MutableStateFlow<String?>(null)
val error: StateFlow<String?> = _error
fun onEmailChange(newEmail: String) {
_email.value = newEmail
_error.value = null
}
fun onPasswordChange(newPassword: String) {
_password.value = newPassword
_error.value = null
}
fun login() {
viewModelScope.launch {
_isLoading.value = true
_error.value = null
val result = authRepository.login(
email = _email.value,
password = _password.value
)
_isLoading.value = false
result.onSuccess { user ->
// 登录成功,导航到主界面
}.onFailure { exception ->
_error.value = when (exception) {
is NetworkException -> "网络连接失败,请检查网络"
is AuthException -> "邮箱或密码错误"
else -> "登录失败,请重试"
}
}
}
}
}
五、导航(Navigation Component)
Kotlin
// 1. 导航图定义
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "splash"
) {
composable("splash") {
SplashScreen(
onNavigateToLogin = { navController.navigate("login") },
onNavigateToHome = { navController.navigate("home") }
)
}
composable("login") {
LoginScreen(
onLoginSuccess = { navController.navigate("home") },
onNavigateToRegister = { navController.navigate("register") }
)
}
composable("register") {
RegisterScreen(
onRegisterSuccess = { navController.navigate("home") },
onNavigateToLogin = { navController.navigate("login") }
)
}
composable("home") {
HomeScreen(
onNavigateToProfile = { userId ->
navController.navigate("profile/$userId")
},
onNavigateToSettings = { navController.navigate("settings") }
)
}
composable(
route = "profile/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.IntType })
) { backStackEntry ->
val userId = backStackEntry.arguments?.getInt("userId") ?: 0
ProfileScreen(
userId = userId,
onNavigateBack = { navController.popBackStack() }
)
}
composable("settings") {
SettingsScreen(
onNavigateBack = { navController.popBackStack() }
)
}
}
}
// 2. 带参数的导航
sealed class Screen(val route: String) {
object Splash : Screen("splash")
object Login : Screen("login")
object Register : Screen("register")
object Home : Screen("home")
object Profile : Screen("profile/{userId}") {
fun createRoute(userId: Int) = "profile/$userId"
}
object Settings : Screen("settings")
}
// 3. 导航工具类
class NavigationManager(
private val navController: NavHostController
) {
fun navigateToLogin() {
navController.navigate(Screen.Login.route) {
popUpTo(Screen.Splash.route) { inclusive = true }
}
}
fun navigateToHome() {
navController.navigate(Screen.Home.route) {
popUpTo(0) { inclusive = true }
}
}
fun navigateToProfile(userId: Int) {
navController.navigate(Screen.Profile.createRoute(userId))
}
fun navigateBack() {
navController.popBackStack()
}
fun navigateUp() {
navController.navigateUp()
}
}
// 4. 在ViewModel中使用导航
class HomeViewModel @Inject constructor(
private val navigationManager: NavigationManager
) : ViewModel() {
fun onProfileClick(userId: Int) {
navigationManager.navigateToProfile(userId)
}
fun onSettingsClick() {
navigationManager.navigateTo(Screen.Settings.route)
}
fun onBackPressed() {
navigationManager.navigateBack()
}
}
// 5. 深链接处理
composable(
route = "details/{id}",
arguments = listOf(navArgument("id") { type = NavType.StringType }),
deepLinks = listOf(
navDeepLink { uriPattern = "myapp://details/{id}" }
)
) { backStackEntry ->
val id = backStackEntry.arguments?.getString("id")
DetailsScreen(id = id)
}
// 6. 底部导航
@Composable
fun MainScreen() {
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
BottomNavigationItem(
icon = { Icon(Icons.Filled.Home, contentDescription = "首页") },
label = { Text("首页") },
selected = currentDestination?.route == "home",
onClick = {
navController.navigate("home") {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
BottomNavigationItem(
icon = { Icon(Icons.Filled.Search, contentDescription = "搜索") },
label = { Text("搜索") },
selected = currentDestination?.route == "search",
onClick = {
navController.navigate("search") {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
BottomNavigationItem(
icon = { Icon(Icons.Filled.Person, contentDescription = "我的") },
label = { Text("我的") },
selected = currentDestination?.route == "profile",
onClick = {
navController.navigate("profile") {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
) { paddingValues ->
NavHost(
navController = navController,
startDestination = "home",
modifier = Modifier.padding(paddingValues)
) {
composable("home") { HomeScreen() }
composable("search") { SearchScreen() }
composable("profile") { ProfileScreen() }
}
}
}
// 7. 嵌套导航图
fun NavGraphBuilder.mainGraph(navController: NavController) {
navigation(startDestination = "dashboard", route = "main") {
composable("dashboard") { DashboardScreen() }
composable("notifications") { NotificationsScreen() }
composable("messages") { MessagesScreen() }
}
}
fun NavGraphBuilder.authGraph(navController: NavController) {
navigation(startDestination = "login", route = "auth") {
composable("login") { LoginScreen() }
composable("register") { RegisterScreen() }
composable("forgotPassword") { ForgotPasswordScreen() }
}
}
// 在主NavHost中使用
NavHost(navController, startDestination = "splash") {
composable("splash") { SplashScreen() }
authGraph(navController)
mainGraph(navController)
}
六、依赖注入(Hilt)
Kotlin
// 1. Application类
@HiltAndroidApp
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 初始化代码
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
}
}
// 2. 定义模块
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
})
.addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer ${getToken()}")
.build()
chain.proceed(request)
}
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"app_database"
)
.fallbackToDestructiveMigration()
.build()
}
@Provides
@Singleton
fun provideUserDao(database: AppDatabase): UserDao {
return database.userDao()
}
@Provides
@Singleton
fun provideUserRepository(
apiService: ApiService,
userDao: UserDao
): UserRepository {
return UserRepositoryImpl(apiService, userDao)
}
}
// 3. ViewModel注入
@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// ViewModel逻辑
}
// 4. Activity/Fragment注入
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var analytics: AnalyticsService
@Inject
lateinit var userManager: UserManager
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 依赖已自动注入
}
}
@AndroidEntryPoint
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
@Inject
lateinit var imageLoader: ImageLoader
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 依赖已自动注入
return super.onCreateView(inflater, container, savedInstanceState)
}
}
// 5. 自定义作用域
@InstallIn(ActivityComponent::class)
@Module
object ActivityModule {
@Provides
@ActivityScoped
fun provideActivityContext(@ActivityContext context: Context): Context {
return context
}
}
@InstallIn(ViewComponent::class)
@Module
object ViewModule {
@Provides
@ViewScoped
fun provideViewDependency(): ViewDependency {
return ViewDependency()
}
}
// 6. 限定符
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class ApiUrl
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class AuthInterceptorOkHttpClient
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@ApiUrl
fun provideApiUrl(): String = BuildConfig.BASE_URL
@Provides
@Singleton
@AuthInterceptorOkHttpClient
fun provideAuthOkHttpClient(
authInterceptor: AuthInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(authInterceptor)
.build()
}
@Provides
@Singleton
fun provideRetrofit(
@ApiUrl baseUrl: String,
@AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
): Retrofit {
return Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.build()
}
}
七、实用工具类
Kotlin
// 1. 网络状态检查
object NetworkUtils {
@SuppressLint("MissingPermission")
fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val network = connectivityManager.activeNetwork
val capabilities = connectivityManager.getNetworkCapabilities(network)
return capabilities != null && (
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
)
} else {
@Suppress("DEPRECATION")
val networkInfo = connectivityManager.activeNetworkInfo
return networkInfo != null && networkInfo.isConnected
}
}
fun getNetworkType(context: Context): String {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
as ConnectivityManager
return when {
connectivityManager.isActiveNetworkMetered -> "MOBILE"
connectivityManager.isActiveNetworkWifi -> "WIFI"
else -> "UNKNOWN"
}
}
// 2. 权限处理
object PermissionUtils {
// 检查单个权限
fun Context.hasPermission(permission: String): Boolean {
return ContextCompat.checkSelfPermission(this, permission) ==
PackageManager.PERMISSION_GRANTED
}
// 检查多个权限
fun Context.hasPermissions(vararg permissions: String): Boolean {
return permissions.all { hasPermission(it) }
}
// 请求权限的ActivityResult合约
class RequestPermissions : ActivityResultContract<Array<String>, Map<String, Boolean>>() {
override fun createIntent(context: Context, input: Array<String>): Intent {
return Intent(ACTION_REQUEST_PERMISSIONS).putExtra(EXTRA_PERMISSIONS, input)
}
override fun parseResult(resultCode: Int, intent: Intent?): Map<String, Boolean> {
if (resultCode != Activity.RESULT_OK) return emptyMap()
val permissions = intent?.getStringArrayExtra(EXTRA_PERMISSIONS) ?: return emptyMap()
val grantResults = intent.getIntArrayExtra(EXTRA_GRANT_RESULTS) ?: return emptyMap()
return permissions.zip(grantResults.map { it == PackageManager.PERMISSION_GRANTED }).toMap()
}
companion object {
const val ACTION_REQUEST_PERMISSIONS = "request_permissions"
const val EXTRA_PERMISSIONS = "permissions"
const val EXTRA_GRANT_RESULTS = "grant_results"
}
}
// 在Activity/Fragment中使用
class MainActivity : AppCompatActivity() {
private val requestPermissions = registerForActivityResult(
RequestPermissions()
) { results ->
if (results.all { it.value }) {
// 所有权限都已授予
startCamera()
} else {
// 有些权限被拒绝
showPermissionDeniedDialog()
}
}
private fun checkAndRequestPermissions() {
val permissions = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
)
if (hasPermissions(*permissions)) {
startCamera()
} else {
requestPermissions.launch(permissions)
}
}
}
}
// 3. 文件操作工具
object FileUtils {
// 获取应用缓存目录
fun Context.getCacheDirPath(): String {
return cacheDir.absolutePath
}
// 获取应用文件目录
fun Context.getFilesDirPath(): String {
return filesDir.absolutePath
}
// 获取外部存储目录
fun Context.getExternalFilesDirPath(type: String? = null): String? {
return getExternalFilesDir(type)?.absolutePath
}
// 检查存储权限
fun Context.hasStoragePermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) &&
hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
// 保存文本到文件
fun Context.saveTextToFile(text: String, fileName: String): Boolean {
return try {
openFileOutput(fileName, Context.MODE_PRIVATE).use { output ->
output.write(text.toByteArray())
}
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
// 从文件读取文本
fun Context.readTextFromFile(fileName: String): String? {
return try {
openFileInput(fileName).use { input ->
input.bufferedReader().use { reader ->
reader.readText()
}
}
} catch (e: Exception) {
e.printStackTrace()
null
}
}
// 保存Bitmap到文件
fun Context.saveBitmapToFile(bitmap: Bitmap, fileName: String): Boolean {
return try {
openFileOutput(fileName, Context.MODE_PRIVATE).use { output ->
bitmap.compress(Bitmap.CompressFormat.PNG, 100, output)
}
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
// 从文件读取Bitmap
fun Context.readBitmapFromFile(fileName: String): Bitmap? {
return try {
BitmapFactory.decodeStream(openFileInput(fileName))
} catch (e: Exception) {
e.printStackTrace()
null
}
}
// 获取文件大小
fun getFileSize(file: File): String {
val sizeInBytes = file.length()
return when {
sizeInBytes < 1024 -> "$sizeInBytes B"
sizeInBytes < 1024 * 1024 -> "${sizeInBytes / 1024} KB"
else -> "${sizeInBytes / (1024 * 1024)} MB"
}
}
// 清理缓存
fun Context.clearCache() {
try {
cacheDir.deleteRecursively()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
// 4. 图片处理工具
object ImageUtils {
// 加载网络图片(使用Coil)
fun ImageView.loadUrl(
url: String,
placeholder: Int = R.drawable.placeholder,
error: Int = R.drawable.error
) {
load(url) {
placeholder(placeholder)
error(error)
crossfade(true)
}
}
// 加载本地图片
fun ImageView.loadFile(
file: File,
placeholder: Int = R.drawable.placeholder,
error: Int = R.drawable.error
) {
load(file) {
placeholder(placeholder)
error(error)
}
}
// 加载资源图片
fun ImageView.loadResource(
@DrawableRes resId: Int,
placeholder: Int = R.drawable.placeholder,
error: Int = R.drawable.error
) {
load(resId) {
placeholder(placeholder)
error(error)
}
}
// 圆形图片
fun ImageView.loadCircleImage(url: String) {
load(url) {
transformations(CircleCropTransformation())
}
}
// 圆角图片
fun ImageView.loadRoundedImage(
url: String,
cornerRadius: Float = 8.dp
) {
load(url) {
transformations(RoundedCornersTransformation(cornerRadius))
}
}
// 高斯模糊
fun ImageView.loadBlurImage(
url: String,
radius: Float = 10f,
sampling: Float = 1f
) {
load(url) {
transformations(BlurTransformation(context, radius, sampling))
}
}
// 保存图片到相册
fun Context.saveImageToGallery(bitmap: Bitmap, title: String): Uri? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
saveImageToGalleryApi29(bitmap, title)
} else {
saveImageToGalleryLegacy(bitmap, title)
}
}
@RequiresApi(Build.VERSION_CODES.Q)
private fun Context.saveImageToGalleryApi29(bitmap: Bitmap, title: String): Uri? {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "$title.jpg")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
}
return contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)?.also { uri ->
contentResolver.openOutputStream(uri)?.use { output ->
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output)
}
}
}
@SuppressLint("MissingPermission")
private fun Context.saveImageToGalleryLegacy(bitmap: Bitmap, title: String): Uri? {
val imagesDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES
)
if (!imagesDir.exists()) {
imagesDir.mkdirs()
}
val imageFile = File(imagesDir, "$title.jpg")
return try {
FileOutputStream(imageFile).use { output ->
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output)
}
MediaScannerConnection.scanFile(
this,
arrayOf(imageFile.absolutePath),
arrayOf("image/jpeg"),
null
)
Uri.fromFile(imageFile)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
// 压缩图片
fun compressImage(
context: Context,
imageUri: Uri,
maxWidth: Int = 1024,
maxHeight: Int = 1024,
quality: Int = 80
): Bitmap? {
return try {
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
context.contentResolver.openInputStream(imageUri)?.use { input ->
BitmapFactory.decodeStream(input, null, options)
}
// 计算采样率
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight)
options.inJustDecodeBounds = false
context.contentResolver.openInputStream(imageUri)?.use { input ->
BitmapFactory.decodeStream(input, null, options)
}
} catch (e: Exception) {
e.printStackTrace()
null
}
}
private fun calculateInSampleSize(
options: BitmapFactory.Options,
reqWidth: Int,
reqHeight: Int
): Int {
val height = options.outHeight
val width = options.outWidth
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight = height / 2
val halfWidth = width / 2
while ((halfHeight / inSampleSize) >= reqHeight &&
(halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
// 创建圆形Bitmap
fun createCircleBitmap(bitmap: Bitmap): Bitmap {
val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
val paint = Paint().apply {
isAntiAlias = true
}
val radius = min(bitmap.width, bitmap.height) / 2f
canvas.drawCircle(
bitmap.width / 2f,
bitmap.height / 2f,
radius,
paint
)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, 0f, 0f, paint)
return output
}
// 创建圆角Bitmap
fun createRoundedBitmap(bitmap: Bitmap, cornerRadius: Float): Bitmap {
val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
val paint = Paint().apply {
isAntiAlias = true
}
val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat())
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, 0f, 0f, paint)
return output
}
// 调整Bitmap大小
fun resizeBitmap(bitmap: Bitmap, newWidth: Int, newHeight: Int): Bitmap {
return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true)
}
// 获取图片方向
fun getImageOrientation(context: Context, imageUri: Uri): Int {
return try {
context.contentResolver.openInputStream(imageUri)?.use { input ->
ExifInterface(input).getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
)
} ?: ExifInterface.ORIENTATION_NORMAL
} catch (e: Exception) {
e.printStackTrace()
ExifInterface.ORIENTATION_NORMAL
}
}
// 旋转Bitmap
fun rotateBitmap(bitmap: Bitmap, degrees: Float): Bitmap {
val matrix = Matrix().apply {
postRotate(degrees)
}
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
}
5. SharedPreferences扩展
Kotlin
// SharedPreferences扩展函数
inline fun <reified T> SharedPreferences.get(
key: String,
defaultValue: T
): T {
return when (T::class) {
String::class -> getString(key, defaultValue as? String) as T
Int::class -> getInt(key, defaultValue as? Int ?: 0) as T
Long::class -> getLong(key, defaultValue as? Long ?: 0L) as T
Float::class -> getFloat(key, defaultValue as? Float ?: 0f) as T
Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T
Set::class -> getStringSet(key, defaultValue as? Set<String>) as T
else -> throw IllegalArgumentException("Unsupported type")
}
}
inline fun <reified T> SharedPreferences.Editor.put(
key: String,
value: T
): SharedPreferences.Editor {
return when (T::class) {
String::class -> putString(key, value as String)
Int::class -> putInt(key, value as Int)
Long::class -> putLong(key, value as Long)
Float::class -> putFloat(key, value as Float)
Boolean::class -> putBoolean(key, value as Boolean)
Set::class -> putStringSet(key, value as Set<String>)
else -> throw IllegalArgumentException("Unsupported type")
}
}
// 数据存储管理器
class PreferenceManager(private val context: Context) {
private val sharedPreferences: SharedPreferences by lazy {
context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
}
// 用户相关
var authToken: String?
get() = sharedPreferences.getString("auth_token", null)
set(value) = sharedPreferences.edit { putString("auth_token", value) }
var userId: Long
get() = sharedPreferences.getLong("user_id", 0L)
set(value) = sharedPreferences.edit { putLong("user_id", value) }
var userName: String?
get() = sharedPreferences.getString("user_name", null)
set(value) = sharedPreferences.edit { putString("user_name", value) }
var userEmail: String?
get() = sharedPreferences.getString("user_email", null)
set(value) = sharedPreferences.edit { putString("user_email", value) }
// 应用设置
var isDarkMode: Boolean
get() = sharedPreferences.getBoolean("dark_mode", false)
set(value) = sharedPreferences.edit { putBoolean("dark_mode", value) }
var language: String
get() = sharedPreferences.getString("language", "zh") ?: "zh"
set(value) = sharedPreferences.edit { putString("language", value) }
var notificationEnabled: Boolean
get() = sharedPreferences.getBoolean("notification_enabled", true)
set(value) = sharedPreferences.edit { putBoolean("notification_enabled", value) }
// 首次启动
var isFirstLaunch: Boolean
get() = sharedPreferences.getBoolean("first_launch", true)
set(value) = sharedPreferences.edit { putBoolean("first_launch", value) }
// 清除所有数据
fun clearAll() {
sharedPreferences.edit().clear().apply()
}
// 清除用户相关数据
fun clearUserData() {
sharedPreferences.edit {
remove("auth_token")
remove("user_id")
remove("user_name")
remove("user_email")
}.apply()
}
}
// 使用示例
class MainActivity : AppCompatActivity() {
private val preferenceManager by lazy { PreferenceManager(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 检查是否首次启动
if (preferenceManager.isFirstLaunch) {
showWelcomeScreen()
preferenceManager.isFirstLaunch = false
}
// 应用主题
if (preferenceManager.isDarkMode) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
}
// 检查登录状态
if (preferenceManager.authToken != null) {
navigateToMainScreen()
} else {
navigateToLoginScreen()
}
}
fun onLoginSuccess(token: String, user: User) {
preferenceManager.authToken = token
preferenceManager.userId = user.id
preferenceManager.userName = user.name
preferenceManager.userEmail = user.email
}
fun onLogout() {
preferenceManager.clearUserData()
navigateToLoginScreen()
}
}