作为一名从Java转向Kotlin的开发者,我深刻体会到Kotlin在面向对象编程方面的简洁与强大。今天我想分享在实际项目中积累的Kotlin类和对象使用经验,这些内容帮助我们的团队将代码量减少了30%的同时提升了可维护性。
Kotlin的类系统看似简单,实则暗藏玄机。与Java不同,Kotlin通过精心的设计消除了大量样板代码,让开发者能更专注于业务逻辑。本文适合有以下基础的读者:
我们将从最基础的类定义开始,逐步深入到属性委托、伴生对象等高级特性,每个环节都会配有我在实际项目中的使用案例和踩坑经验。
Kotlin中使用class关键字定义类,这是最基础的语法:
kotlin复制class Person {
var name: String = ""
var age: Int = 0
fun introduce() {
println("Hi, I'm $name, $age years old.")
}
}
这段看似简单的代码其实有几个关键点需要注意:
提示:在Android开发中,建议对实体类使用
data class,对工具类使用object,普通class通常用于需要继承的场景。
Kotlin的构造函数分为主构造函数和次构造函数:
kotlin复制class Person(
val name: String, // 使用val使属性不可变
var age: Int // 使用var使属性可变
) {
// 次构造函数必须直接或间接委托给主构造函数
constructor(name: String) : this(name, 0) {
println("Secondary constructor called")
}
init {
require(age >= 0) { "Age cannot be negative" }
}
}
实际项目经验:
init块进行参数验证创建对象时各部分的执行顺序很重要:
kotlin复制val person = Person("Alice", 25)
初始化顺序:
这个顺序在复杂的类继承关系中尤为重要,我在一个项目中就曾因为不了解这个顺序导致NPE(空指针异常)。
Kotlin的数据类(data class)可以自动生成:
kotlin复制data class User(
val id: Long,
val name: String,
val email: String
)
// 使用示例
val user1 = User(1, "Alice", "alice@example.com")
val user2 = user1.copy(name = "Alice Smith") // 复制并修改部分属性
注意:数据类的主构造函数必须至少有一个参数,且所有参数必须标记为val或var。
密封类(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表达式处理所有可能情况
fun handleResult(result: Result<String>) {
when(result) {
is Result.Success -> println(result.data)
is Result.Error -> println(result.exception)
Result.Loading -> println("Loading...")
}
}
实际项目中的应用场景:
Kotlin没有static关键字,而是使用伴生对象:
kotlin复制class DatabaseClient {
companion object {
private const val MAX_CONNECTIONS = 10
fun create(): DatabaseClient {
return DatabaseClient()
}
}
}
// 调用方式
val client = DatabaseClient.create()
伴生对象的特点:
Kotlin属性背后是字段+访问器的组合:
kotlin复制class Person(name: String) {
var name: String = name
get() = field.capitalize()
set(value) {
field = value.trim()
}
var age: Int = 0
private set // 只能在类内部修改
}
我在项目中常用的模式:
Kotlin标准库提供了几种有用的属性委托:
kotlin复制class Example {
var observableProperty: String by Delegates.observable("<none>") {
prop, old, new ->
println("$old -> $new")
}
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
}
自定义属性委托:
kotlin复制class PreferenceDelegate<T>(
private val context: Context,
private val key: String,
private val defaultValue: T
) : ReadWriteProperty<Any?, T> {
// 实现getValue和setValue
}
// 使用示例
class Settings(context: Context) {
var darkMode by PreferenceDelegate(context, "dark_mode", false)
}
kotlin复制class User {
val name: String
val description: String = "User: $name" // 错误!name还未初始化
constructor(name: String) {
this.name = name
}
}
解决方案:
kotlin复制open class Entity(val id: Long)
data class User(val name: String) : Entity(0) // 不会生成equals/hashCode考虑父类属性
解决方案:
Java代码调用Kotlin伴生对象成员:
java复制// Kotlin
class Foo {
companion object {
fun bar() {}
}
}
// Java调用
Foo.Companion.bar(); // 默认方式
Foo.bar(); // 需要@JvmStatic注解
最佳实践:
Kotlin 1.3引入的内联类可以减少包装类的开销:
kotlin复制@JvmInline
value class Password(private val s: String)
// 运行时会被当作String处理,但编译时是独立类型
使用场景:
对于频繁创建的对象,可以考虑对象池:
kotlin复制object ParserPool {
private val pool = mutableListOf<JsonParser>()
fun acquire(): JsonParser {
return pool.removeLastOrNull() ?: JsonParser()
}
fun release(parser: JsonParser) {
pool.add(parser)
}
}
属性委托虽然方便,但会引入额外开销:
kotlin复制// 不推荐 - 每个属性都会创建委托实例
class User {
var name by Delegates.observable("") { _, _, _ -> }
var age by Delegates.observable(0) { _, _, _ -> }
}
// 推荐 - 手动实现观察逻辑
class User {
var name: String = ""
set(value) {
field = value
onPropertyChanged()
}
private fun onPropertyChanged() {
// 统一处理变更逻辑
}
}
在Android开发中,我发现合理使用这些技巧可以将内存分配减少15-20%,特别是在列表滚动等高频操作场景下效果明显。