1. Kotlin类定义基础与核心语法
Kotlin作为现代JVM语言的代表,其类定义系统在保持简洁性的同时提供了强大的表达能力。与Java相比,Kotlin的类声明更加精炼,一个完整的类定义可以简单到只需一行代码:
kotlin复制class Person(val name: String, var age: Int)
这行代码实际上完成了以下工作:
- 声明了名为Person的类
- 定义了不可变属性name(val修饰)
- 定义了可变属性age(var修饰)
- 自动生成了构造函数
- 自动生成getter/setter(对于var属性)
类的实例化也极为简单,不需要new关键字:
kotlin复制val person = Person("张三", 25)
注意:Kotlin中默认所有类都是final的,无法被继承。如果需要允许继承,必须显式添加
open修饰符。
1.1 构造函数的多形态表达
Kotlin的构造函数分为主构造函数和次构造函数两种形式:
主构造函数是类头的一部分,紧跟在类名后面:
kotlin复制class User constructor(_id: Int) {
val id = _id
}
当主构造函数没有注解或可见性修饰符时,constructor关键字可以省略:
kotlin复制class User(_id: Int) {
val id = _id
}
更常见的做法是直接在类头中声明属性:
kotlin复制class User(val id: Int)
次构造函数使用constructor关键字定义在类体内:
kotlin复制class Person(val name: String) {
var age: Int = 0
var address: String = ""
constructor(name: String, age: Int) : this(name) {
this.age = age
}
constructor(name: String, age: Int, address: String) : this(name, age) {
this.address = address
}
}
重要规则:每个次构造函数必须直接或间接委托给主构造函数(通过
this()调用)
1.2 属性与字段的进阶用法
Kotlin中的属性声明会自动生成对应的getter和setter方法。我们可以自定义这些访问器的行为:
kotlin复制class Rectangle(val width: Int, val height: Int) {
val area: Int
get() = width * height // 自定义getter
var borderColor: String = "black"
set(value) {
if (value.isNotBlank()) {
field = value // 使用field标识符引用幕后字段
}
}
}
延迟初始化属性在Android开发中特别有用:
kotlin复制class MyActivity : Activity() {
lateinit var adapter: RecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapter = RecyclerViewAdapter(data)
}
}
使用
lateinit时需注意:属性必须是var且不能为可空类型,在访问前必须初始化,否则会抛出UninitializedPropertyAccessException
2. 类的高级特性与特殊类
2.1 数据类的深度解析
数据类(data class)是Kotlin中用于持有数据的特殊类,自动生成以下方法:
- equals()/hashCode()
- toString()
- componentN()函数(用于解构声明)
- copy()
定义数据类:
kotlin复制data class User(val id: Int, val name: String, val email: String)
数据类的实用特性:
- 解构声明:
kotlin复制val (id, name, email) = user
- copy函数:
kotlin复制val updatedUser = user.copy(name = "新名字")
限制:数据类必须满足以下条件
- 主构造函数至少有一个参数
- 所有主构造参数必须标记为val或var
- 不能是abstract、open、sealed或inner
2.2 密封类的实际应用
密封类(sealed class)用于表示受限的类继承结构:
kotlin复制sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
密封类的关键特点:
- 所有子类必须在同一文件中声明
- 本身是抽象类,不能直接实例化
- 与when表达式配合使用时,不需要else分支(编译器能检测是否覆盖所有情况)
kotlin复制fun handleResult(result: Result<String>) {
when(result) {
is Result.Success -> println(result.data)
is Result.Error -> println(result.exception)
Result.Loading -> println("加载中...")
}
}
2.3 对象声明与伴生对象
Kotlin使用object关键字实现单例模式:
kotlin复制object DatabaseManager {
init {
// 初始化代码
}
fun connect() { ... }
}
// 使用
DatabaseManager.connect()
伴生对象(companion object)相当于Java的静态成员,但更灵活:
kotlin复制class MyClass {
companion object {
const val CONSTANT = "常量值"
fun create(): MyClass = MyClass()
}
}
// 调用
val instance = MyClass.create()
提示:伴生对象可以实现接口,这是比Java静态方法更强大的特性
3. 类的关系与继承体系
3.1 继承与多态的实现
Kotlin中所有类默认继承自Any(相当于Java的Object),但不同于Java的是:
- 类默认是final的
- 方法默认是final的
- 属性默认是final的
要使类可继承,必须显式使用open修饰符:
kotlin复制open class Animal(val name: String) {
open fun makeSound() {
println("动物发出声音")
}
}
class Dog(name: String) : Animal(name) {
override fun makeSound() {
println("$name 汪汪叫")
}
}
方法重写也必须显式使用override关键字,且父类方法必须标记为open。
3.2 接口与抽象类的选择
Kotlin接口可以包含:
- 抽象方法
- 默认实现的方法
- 抽象属性
kotlin复制interface Clickable {
fun click() // 抽象方法
fun showOff() = println("我被点击了!") // 带默认实现的方法
}
interface Focusable {
fun showOff() = println("我获得焦点了!")
}
class Button : Clickable, Focusable {
override fun click() {
println("按钮被点击")
}
// 必须重写冲突的默认方法
override fun showOff() {
super<Clickable>.showOff()
super<Focusable>.showOff()
}
}
抽象类与接口的主要区别:
- 抽象类可以有状态(存储属性)
- 抽象类可以有构造方法
- 接口可以实现多重继承
3.3 委托模式的优雅实现
Kotlin原生支持委托模式,通过by关键字实现:
kotlin复制interface IRepository {
fun save(data: String)
}
class DatabaseRepository : IRepository {
override fun save(data: String) {
println("保存到数据库: $data")
}
}
class LoggerRepository(private val repository: IRepository) : IRepository by repository {
override fun save(data: String) {
println("准备保存: $data")
repository.save(data)
println("保存完成")
}
}
类属性委托的典型应用:
kotlin复制class Example {
var p: String by Delegate()
}
class Delegate {
private var storedValue: String = ""
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("获取属性 ${property.name}")
return storedValue
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("设置属性 ${property.name} = $value")
storedValue = value
}
}
4. Kotlin类在Android开发中的实战应用
4.1 ViewModel的最佳实践
在Android中使用Kotlin定义ViewModel:
kotlin复制class MainViewModel : ViewModel() {
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data
fun loadData() {
viewModelScope.launch {
_data.value = repository.fetchData()
}
}
}
使用属性委托简化LiveData观察:
kotlin复制class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.data.observe(this) { data ->
// 更新UI
}
}
}
4.2 RecyclerView.Adapter的Kotlin实现
使用Kotlin简化Adapter实现:
kotlin复制class UserAdapter(private val users: List<User>) :
RecyclerView.Adapter<UserAdapter.ViewHolder>() {
inner class ViewHolder(val binding: ItemUserBinding) :
RecyclerView.ViewHolder(binding.root)
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.binding.apply {
user = users[position]
executePendingBindings()
}
}
override fun getItemCount() = users.size
}
结合扩展函数进一步简化:
kotlin复制fun <T : ViewBinding> RecyclerView.Adapter<*>.createBinding(
parent: ViewGroup,
inflate: (LayoutInflater, ViewGroup, Boolean) -> T
): T {
return inflate(LayoutInflater.from(parent.context), parent, false)
}
4.3 使用Kotlin优化网络请求
定义API接口:
kotlin复制interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") id: Int): User
@POST("users")
suspend fun createUser(@Body user: User): Response<Unit>
}
Repository实现:
kotlin复制class UserRepository @Inject constructor(
private val api: ApiService,
private val cache: UserCache
) {
suspend fun getUser(id: Int): User {
return try {
val user = api.getUser(id)
cache.saveUser(user)
user
} catch (e: Exception) {
cache.getUser(id) ?: throw e
}
}
}
结合协程的ViewModel:
kotlin复制class UserViewModel @ViewModelInject constructor(
private val repository: UserRepository
) : ViewModel() {
private val _user = MutableLiveData<Result<User>>()
val user: LiveData<Result<User>> = _user
fun loadUser(id: Int) {
viewModelScope.launch {
_user.value = Result.Loading
_user.value = try {
Result.Success(repository.getUser(id))
} catch (e: Exception) {
Result.Error(e)
}
}
}
}
5. 性能优化与常见问题排查
5.1 对象创建的性能考量
- 内联类(inline class):减少包装类的开销
kotlin复制@JvmInline
value class Password(val value: String)
- 对象池技术:
kotlin复制object BitmapPool {
private val pool = mutableMapOf<String, Bitmap>()
fun get(key: String): Bitmap? = pool[key]
fun put(key: String, bitmap: Bitmap) {
pool.getOrPut(key) { bitmap }
}
}
- 避免不必要的对象创建:
kotlin复制// 不好的写法 - 每次调用都创建新Formatter
fun formatDate(date: Date): String {
val formatter = SimpleDateFormat("yyyy-MM-dd")
return formatter.format(date)
}
// 改进写法 - 重用Formatter
private val dateFormatter = SimpleDateFormat("yyyy-MM-dd")
fun formatDate(date: Date): String = dateFormatter.format(date)
5.2 内存泄漏检测与预防
常见内存泄漏场景及解决方案:
- Activity泄漏:
kotlin复制class MyActivity : Activity() {
private val heavyObject = HeavyObject()
override fun onCreate(savedInstanceState: Bundle?) {
// 错误示例 - 匿名内部类持有Activity引用
heavyObject.setListener(object : HeavyListener {
override fun onEvent() {
updateUI() // 隐式持有Activity引用
}
})
}
// 正确做法 - 使用弱引用或及时解绑
private val listener = object : HeavyListener {
override fun onEvent() {
updateUI()
}
}
override fun onDestroy() {
heavyObject.removeListener(listener)
super.onDestroy()
}
}
- ViewModel泄漏:
kotlin复制class MyViewModel : ViewModel() {
private val scope = CoroutineScope(Dispatchers.IO) // 错误 - 创建额外作用域
// 正确做法 - 使用viewModelScope
fun loadData() {
viewModelScope.launch {
// 网络请求
}
}
override fun onCleared() {
scope.cancel() // 必须手动取消
super.onCleared()
}
}
5.3 多线程安全实践
- 协程并发控制:
kotlin复制class ConcurrentProcessor {
private val mutex = Mutex()
var sharedData = 0
suspend fun safeIncrement() {
mutex.withLock {
sharedData++
}
}
}
- 线程安全集合:
kotlin复制val concurrentMap = ConcurrentHashMap<String, String>()
val synchronizedList = Collections.synchronizedList(mutableListOf<String>())
- @Volatile注解:
kotlin复制class Singleton {
@Volatile
private var instance: Singleton? = null
fun getInstance(): Singleton {
return instance ?: synchronized(this) {
instance ?: Singleton().also { instance = it }
}
}
}
6. Kotlin与Java互操作的最佳实践
6.1 空安全类型的兼容处理
Java代码调用Kotlin空安全API时的处理:
kotlin复制@JvmName("nullableList")
fun getListOrNull(): List<String>? = null
@JvmName("nonNullList")
fun getList(): List<String> = listOf()
Kotlin调用Java代码的空安全处理:
java复制// Java代码
public class JavaUtils {
public static String getString() {
return null; // 可能返回null
}
}
kotlin复制// Kotlin调用
val str: String = JavaUtils.getString() ?: "" // 必须处理可能的null
6.2 伴生对象的Java调用
Kotlin伴生对象在Java中的访问方式:
kotlin复制class KotlinClass {
companion object {
const val CONSTANT = "value"
fun utilityMethod() { ... }
}
}
Java中调用:
java复制// 访问常量
String value = KotlinClass.CONSTANT;
// 调用方法
KotlinClass.Companion.utilityMethod();
可以使用@JvmStatic优化Java调用体验:
kotlin复制companion object {
@JvmStatic
fun utilityMethod() { ... }
}
6.3 异常处理的互操作
Kotlin没有受检异常,与Java交互时需要注意:
kotlin复制@Throws(IOException::class)
fun readFile(path: String): String {
// 可能抛出IOException
}
Java中调用:
java复制try {
String content = KotlinFileReader.readFile("path.txt");
} catch (IOException e) {
// 必须捕获受检异常
}
7. Kotlin DSL与领域特定语言
7.1 类型安全的构建器模式
创建HTML DSL示例:
kotlin复制fun html(block: HTML.() -> Unit): HTML {
return HTML().apply(block)
}
class HTML {
fun head(block: Head.() -> Unit) {
Head().apply(block)
}
fun body(block: Body.() -> Unit) {
Body().apply(block)
}
}
class Head {
fun title(text: String) {
println("<title>$text</title>")
}
}
class Body {
fun p(block: P.() -> Unit) {
P().apply(block)
}
}
class P {
var text = ""
operator fun String.unaryPlus() {
text += this
}
}
// 使用
html {
head {
title("Kotlin DSL示例")
}
body {
p {
+"这是一段"
+"Kotlin DSL"
+"生成的HTML"
}
}
}
7.2 Android布局的DSL实现
使用Kotlin DSL替代XML布局:
kotlin复制interface ViewBuilder {
fun TextView.text(text: String)
fun TextView.textSize(size: Float)
fun View.margin(left: Int = 0, top: Int = 0, right: Int = 0, bottom: Int = 0)
}
class LinearLayoutBuilder(context: Context) : ViewBuilder {
private val layout = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
}
fun build(): LinearLayout = layout
fun TextView(init: TextView.() -> Unit): TextView {
return TextView(layout.context).apply {
init()
layout.addView(this)
}
}
override fun TextView.text(text: String) {
this.text = text
}
override fun TextView.textSize(size: Float) {
this.textSize = size
}
override fun View.margin(left: Int, top: Int, right: Int, bottom: Int) {
val params = layoutParams as? LinearLayout.LayoutParams
?: LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
params.setMargins(left, top, right, bottom)
layoutParams = params
}
}
// 使用
val layout = LinearLayoutBuilder(context).apply {
TextView {
text = "Hello Kotlin DSL"
textSize = 18f
margin(top = 16)
}
}.build()
8. Kotlin元编程与反射
8.1 KClass的基本操作
获取和使用KClass:
kotlin复制val stringClass = String::class
val listClass = List::class
// 获取Java Class
val javaClass = stringClass.java
// 创建实例
val constructor = String::class.primaryConstructor
val emptyString = constructor?.call()
// 获取所有成员
class MyClass(val prop: String) {
fun function() {}
}
MyClass::class.members.forEach { member ->
println(member.name)
}
8.2 属性反射的实用技巧
访问和修改属性:
kotlin复制class Person(var name: String, var age: Int)
val person = Person("Alice", 25)
val nameProperty = Person::name
println(nameProperty.get(person)) // 输出 Alice
nameProperty.set(person, "Bob")
println(person.name) // 输出 Bob
注解处理示例:
kotlin复制@Target(AnnotationTarget.PROPERTY)
annotation class Important
class Config {
@Important
val apiKey: String = "secret"
val endpoint: String = "https://api.example.com"
}
fun getImportantProperties(config: Any): List<String> {
return config::class.memberProperties
.filter { it.annotations.any { ann -> ann is Important } }
.map { it.name }
}
// 使用
val importantProps = getImportantProperties(Config())
println(importantProps) // 输出 [apiKey]
8.3 使用反射实现依赖注入
简单DI容器实现:
kotlin复制class DIContainer {
private val instances = mutableMapOf<KClass<*>, Any>()
fun <T : Any> register(clazz: KClass<T>, instance: T) {
instances[clazz] = instance
}
fun <T : Any> resolve(clazz: KClass<T>): T {
return instances[clazz] as? T ?: createInstance(clazz)
}
private fun <T : Any> createInstance(clazz: KClass<T>): T {
val constructor = clazz.primaryConstructor
?: throw IllegalArgumentException("类必须有一个主构造函数")
val parameters = constructor.parameters.map { param ->
resolve(param.type.classifier as KClass<*>)
}
return constructor.call(*parameters.toTypedArray())
}
}
// 使用示例
interface Service {
fun doWork()
}
class ServiceImpl : Service {
override fun doWork() {
println("工作完成")
}
}
class Client(private val service: Service) {
fun execute() {
service.doWork()
}
}
// 注册和解析
val container = DIContainer()
container.register(Service::class, ServiceImpl())
val client = container.resolve(Client::class)
client.execute() // 输出 "工作完成"
9. Kotlin协程与类的结合
9.1 协程作用域的管理
在类中正确管理协程作用域:
kotlin复制class DataProcessor {
private val processorScope = CoroutineScope(
Dispatchers.Default + SupervisorJob()
)
fun processData(data: List<Int>, callback: (Result) -> Unit) {
processorScope.launch {
try {
val result = heavyProcessing(data)
withContext(Dispatchers.Main) {
callback(Result.Success(result))
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
callback(Result.Error(e))
}
}
}
}
private suspend fun heavyProcessing(data: List<Int>): Long {
return withContext(Dispatchers.Default) {
delay(1000) // 模拟耗时操作
data.sum().toLong()
}
}
fun destroy() {
processorScope.cancel()
}
}
9.2 Flow在类中的使用
使用Flow实现数据流:
kotlin复制class TemperatureSensor {
private val _temperatureFlow = MutableStateFlow(0f)
val temperatureFlow: StateFlow<Float> = _temperatureFlow
init {
CoroutineScope(Dispatchers.IO).launch {
while (true) {
delay(1000)
_temperatureFlow.value = readSensorValue()
}
}
}
private fun readSensorValue(): Float {
// 模拟传感器读数
return Random.nextFloat() * 100
}
}
// 使用
class TemperatureDisplay : CoroutineScope by MainScope() {
private val sensor = TemperatureSensor()
fun startObserving() {
launch {
sensor.temperatureFlow.collect { temp ->
updateDisplay(temp)
}
}
}
private fun updateDisplay(temp: Float) {
// 更新UI
}
fun destroy() {
cancel()
}
}
9.3 通道(Channel)在类间通信
使用Channel实现类间通信:
kotlin复制class EventBus {
private val _events = Channel<Event>()
val events = _events.receiveAsFlow()
suspend fun sendEvent(event: Event) {
_events.send(event)
}
fun close() {
_events.close()
}
}
class EventHandler(private val eventBus: EventBus) : CoroutineScope by MainScope() {
init {
launch {
eventBus.events.collect { event ->
when (event) {
is LoginEvent -> handleLogin(event)
is LogoutEvent -> handleLogout()
}
}
}
}
private fun handleLogin(event: LoginEvent) {
// 处理登录事件
}
private fun handleLogout() {
// 处理登出事件
}
}
10. Kotlin跨平台开发中的类设计
10.1 共享代码的通用类设计
使用expect/actual机制实现平台特定代码:
kotlin复制// 公共模块
expect class FileHandler(path: String) {
fun read(): String
fun write(content: String)
}
// Android实现
actual class FileHandler actual constructor(private val path: String) {
actual fun read(): String {
return File(path).readText()
}
actual fun write(content: String) {
File(path).writeText(content)
}
}
// iOS实现
actual class FileHandler actual constructor(private val path: String) {
actual fun read(): String {
return NSString(contentsOfFile = path, encoding = NSUTF8StringEncoding) as String
}
actual fun write(content: String) {
(content as NSString).writeToFile(path, atomically = true, encoding = NSUTF8StringEncoding)
}
}
10.2 多平台序列化方案
使用kotlinx.serialization实现跨平台序列化:
kotlin复制@Serializable
data class User(
val id: Int,
val name: String,
@SerialName("created_at")
val createdAt: Instant
)
// 公共序列化方法
expect fun createJson(): Json
// Android实现
actual fun createJson(): Json {
return Json {
ignoreUnknownKeys = true
isLenient = true
}
}
// iOS实现
actual fun createJson(): Json {
return Json {
ignoreUnknownKeys = true
isLenient = true
}
}
// 使用
val json = createJson()
val user = json.decodeFromString<User>(jsonString)
val jsonString = json.encodeToString(user)
10.3 跨平台依赖注入
共享模块中的服务定义:
kotlin复制interface AnalyticsService {
fun trackEvent(event: String, properties: Map<String, Any>)
}
expect fun provideAnalyticsService(): AnalyticsService
// 平台特定实现
// Android
actual fun provideAnalyticsService(): AnalyticsService = AndroidAnalyticsService()
// iOS
actual fun provideAnalyticsService(): AnalyticsService = IOSAnalyticsService()
// 使用
class UserViewModel(
private val analytics: AnalyticsService = provideAnalyticsService()
) {
fun onUserAction() {
analytics.trackEvent("user_action", mapOf("time" to System.currentTimeMillis()))
}
}
11. Kotlin类的高级性能优化
11.1 内联类的使用场景
内联类(Inline classes)可以避免包装类的开销:
kotlin复制@JvmInline
value class Password(private val value: String) {
init {
require(value.length >= 8) { "密码长度至少8位" }
}
fun isValid(): Boolean {
return value.any { it.isDigit() } &&
value.any { it.isLetter() }
}
}
// 使用
fun login(username: String, password: Password) {
if (password.isValid()) {
// 登录逻辑
}
}
login("user", Password("secure123")) // 正确
login("user", Password("short")) // 抛出异常
11.2 使用@JvmOverloads优化Java调用
为Java调用者提供更好的重载体验:
kotlin复制class Dialog @JvmOverloads constructor(
title: String,
message: String,
cancelable: Boolean = true
) {
// 类实现
}
// Java中可以使用
new Dialog("标题", "消息"); // 使用默认cancelable=true
new Dialog("标题", "消息", false);
11.3 使用@JvmName解决签名冲突
当Kotlin中函数名相同但JVM签名相同时:
kotlin复制@JvmName("filterStrings")
fun List<String>.filter(predicate: (String) -> Boolean): List<String> {
return filter(predicate)
}
@JvmName("filterInts")
fun List<Int>.filter(predicate: (Int) -> Boolean): List<Int> {
return filter(predicate)
}
// Java中调用
List<String> strings = KotlinCollections.filterStrings(list, predicate);
List<Integer> ints = KotlinCollections.filterInts(list, predicate);
12. Kotlin类设计的最佳实践
12.1 不变性与线程安全
优先使用不可变类和属性:
kotlin复制class Configuration(
val apiUrl: String,
val timeout: Int,
val retryCount: Int
) {
// 辅助方法可以返回新实例
fun withTimeout(newTimeout: Int): Configuration {
return copy(timeout = newTimeout)
}
private fun copy(
apiUrl: String = this.apiUrl,
timeout: Int = this.timeout,
retryCount: Int = this.retryCount
) = Configuration(apiUrl, timeout, retryCount)
}
12.2 构建器模式的现代替代
使用命名参数和默认参数替代构建器模式:
kotlin复制class HttpClientConfig(
val connectTimeout: Int = 5000,
val readTimeout: Int = 5000,
val writeTimeout: Int = 5000,
val retryOnFailure: Boolean = true,
val logLevel: LogLevel = LogLevel.BASIC
) {
// 可选:提供DSL风格的构建方式
class Builder {
var connectTimeout: Int = 5000
var readTimeout: Int = 5000
var writeTimeout: Int = 5000
var retryOnFailure: Boolean = true
var logLevel: LogLevel = LogLevel.BASIC
fun build() = HttpClientConfig(
connectTimeout,
readTimeout,
writeTimeout,
retryOnFailure,
logLevel
)
}
}
// 使用命名参数
val config1 = HttpClientConfig(
connectTimeout = 10000,
logLevel = LogLevel.DETAILED
)
// 使用构建器
val config2 = HttpClientConfig.Builder()
.apply {
connectTimeout = 10000
logLevel = LogLevel.DETAILED
}
.build()
12.3 领域驱动设计(DDD)实践
使用Kotlin实现DDD中的值对象和实体:
kotlin复制// 值对象 - 通过相等性比较
data class Address(
val street: String,
val city: String,
val postalCode: String
) {
init {
require(street.isNotBlank()) { "街道不能为空" }
require(city.isNotBlank()) { "城市不能为空" }
require(postalCode.matches(Regex("\\d{5}"))) { "邮政编码格式错误" }
}
}
// 实体 - 通过标识符比较
class User(
val id: UUID,
var name: String,
var email: String
) {
init {
require(name.isNotBlank()) { "用户名不能为空" }
require(email.contains('@')) { "邮箱格式错误" }
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is User) return false
return id == other.id
}
override fun hashCode(): Int = id.hashCode()
fun changeEmail(newEmail: String) {
require(newEmail.contains('@')) { "邮箱格式错误" }
email = newEmail
}
}
13. Kotlin测试驱动开发(TDD)实践
13.1 测试友好的类设计
设计易于测试的类:
kotlin复制class OrderProcessor(
private val paymentGateway: PaymentGateway,
private val inventoryService: InventoryService
) {
fun process(order: Order): Result<OrderReceipt> {
return runCatching {
validateOrder(order)
val paymentResult = paymentGateway.charge(order.totalAmount)
inventoryService.updateStock(order.items)
OrderReceipt(order.id, paymentResult.transactionId)
}
}
private fun validateOrder(order: Order) {
require(order.items.isNotEmpty()) { "订单不能为空" }
require(order.totalAmount > 0) { "金额必须大于0" }
}
}
// 测试
class OrderProcessorTest {
private val mockPaymentGateway = mockk<PaymentGateway>()
private val mockInventoryService = mockk<InventoryService>()
private val processor = OrderProcessor(mockPaymentGateway, mockInventoryService)
@Test
fun `process should return receipt for valid order`() {
every { mockPaymentGateway.charge(any()) } returns PaymentResult("tx123")
justRun { mockInventoryService.updateStock(any()) }
val order = createTestOrder()
val result = processor.process(order)
assertTrue(result.isSuccess)
assertEquals("tx123", result.getOrThrow().transactionId)
}
}
13.2 使用MockK进行模拟测试
Kotlin专属的MockK框架使用示例:
kotlin复制class UserServiceTest {
private val mockRepository = mockk<UserRepository>()
private val service = UserService(mockRepository)
@Test
fun `getUser should return user from repository`() {
val expectedUser = User("id", "name")
every { mockRepository.getUser("id") } returns expectedUser
val result = service.getUser("id")
assertEquals(expectedUser, result)
verify { mockRepository.getUser("id") }
}
@Test
fun `saveUser should propagate exception`() {
val exception = RuntimeException("DB error")
every { mockRepository.saveUser(any()) } throws exception
assertThrows<RuntimeException> {
service.saveUser(User("id", "name"))
}
}
}
13.3 属性测试与数据驱动测试
使用Kotest进行属性测试:
kotlin复制class MathSpec : StringSpec({
"addition should be commutative" {
forAll(Gen.int(), Gen.int()) { a, b ->
(a + b) == (b + a)
}
}
"string length should be at least prefix length" {
checkAll(Gen.string(), Gen.string()) { prefix, str ->
(prefix + str).length >= prefix.length
}
}
})
数据驱动测试示例:
kotlin复制class CalculatorTest : FunSpec({
context("Addition") {
withData(
mapOf("a" to 1, "b" to 2, "expected" to 3),
mapOf("a" to -1, "b" to 1, "expected" to 0),
mapOf("a" to 0, "b" to 0, "expected" to 0)
) { (a: Int, b: Int, expected: Int) ->
Calculator.add(a, b) shouldBe expected
}
}
})
14. Kotlin与函数式编程的结合
14.1 不可变数据结构的实现
使用Kotlin实现持久化数据结构:
kotlin复制class PersistentList<T> private constructor(
private val head: T?,
private val tail: PersistentList<T>?
) : Iterable<T> {
companion object {
fun <T> empty(): PersistentList<T> = PersistentList(null, null)
fun <T> of(vararg elements: T): PersistentList<T> {
return elements.foldRight(empty()) { elem, acc ->
acc.add(elem)
}
}
}
fun add(element: T): PersistentList<T> {
return PersistentList(element, this)
}
override fun iterator(): Iterator<T> {
return object : Iterator<T> {
private var current: PersistentList<T>? = this@PersistentList
override fun
