Skip to content

变量和常量

概述

本章详细介绍Kotlin中变量和常量的声明、初始化和使用。Kotlin通过varval关键字来区分可变和不可变的变量,这是函数式编程和不可变性原则的体现。

变量声明

var - 可变变量

kotlin
fun main() {
    // 基本变量声明
    var name = "Kotlin"  // 类型推断为String
    var age: Int = 25    // 显式类型声明
    var height = 175.5   // 类型推断为Double
    
    println("初始值:")
    println("姓名: $name")
    println("年龄: $age")
    println("身高: $height")
    
    // 修改变量值
    name = "Kotlin 1.9"
    age = 26
    height = 176.0
    
    println("\n修改后:")
    println("姓名: $name")
    println("年龄: $age")
    println("身高: $height")
}

val - 不可变变量

kotlin
fun main() {
    // 不可变变量声明
    val language = "Kotlin"
    val version = 1.9
    val isStable = true
    
    println("语言信息:")
    println("语言: $language")
    println("版本: $version")
    println("稳定版: $isStable")
    
    // 尝试修改val变量会导致编译错误
    // language = "Java"  // 编译错误!
    // version = 2.0      // 编译错误!
    
    // val变量必须在声明时或构造函数中初始化
    val currentTime: Long
    currentTime = System.currentTimeMillis()  // 延迟初始化
    println("当前时间: $currentTime")
    
    // currentTime = System.currentTimeMillis()  // 编译错误:已经初始化
}

类型推断

自动类型推断

kotlin
fun main() {
    // Kotlin可以自动推断类型
    val string = "Hello"           // String
    val integer = 42               // Int
    val long = 42L                 // Long
    val double = 3.14              // Double
    val float = 3.14f              // Float
    val boolean = true             // Boolean
    val char = 'A'                 // Char
    
    // 集合类型推断
    val list = listOf(1, 2, 3)     // List<Int>
    val map = mapOf("a" to 1)      // Map<String, Int>
    val set = setOf("x", "y")      // Set<String>
    
    println("类型推断示例:")
    println("string: $string (${string::class.simpleName})")
    println("integer: $integer (${integer::class.simpleName})")
    println("list: $list (${list::class.simpleName})")
}

显式类型声明

kotlin
fun main() {
    // 当需要明确类型时
    val number: Number = 42        // 父类型
    val any: Any = "Hello"         // 顶级类型
    val nullable: String? = null   // 可空类型
    
    // 空集合需要显式类型
    val emptyList: List<String> = emptyList()
    val emptyMap: Map<String, Int> = emptyMap()
    
    // 函数类型
    val function: (Int, Int) -> Int = { a, b -> a + b }
    
    println("显式类型声明:")
    println("number: $number")
    println("any: $any")
    println("nullable: $nullable")
    println("function result: ${function(5, 3)}")
}

常量

编译时常量 (const)

kotlin
// 顶级常量
const val APP_NAME = "MyKotlinApp"
const val VERSION_CODE = 1
const val PI = 3.14159

class MathUtils {
    companion object {
        // 伴生对象中的常量
        const val E = 2.71828
        const val GOLDEN_RATIO = 1.61803
    }
}

fun main() {
    println("应用常量:")
    println("应用名称: $APP_NAME")
    println("版本号: $VERSION_CODE")
    println("圆周率: $PI")
    println("自然常数: ${MathUtils.E}")
    println("黄金比例: ${MathUtils.GOLDEN_RATIO}")
}

运行时常量

kotlin
class Configuration {
    companion object {
        // 运行时确定的常量
        val START_TIME = System.currentTimeMillis()
        val USER_HOME = System.getProperty("user.home")
        val RANDOM_ID = java.util.UUID.randomUUID().toString()
    }
}

fun main() {
    println("运行时常量:")
    println("启动时间: ${Configuration.START_TIME}")
    println("用户主目录: ${Configuration.USER_HOME}")
    println("随机ID: ${Configuration.RANDOM_ID}")
}

变量作用域

局部变量

kotlin
fun demonstrateScope() {
    val outerVariable = "外部变量"
    
    if (true) {
        val innerVariable = "内部变量"
        println("内部作用域可以访问: $outerVariable")
        println("内部变量: $innerVariable")
    }
    
    // println(innerVariable)  // 编译错误:无法访问内部变量
    
    // 循环作用域
    for (i in 1..3) {
        val loopVariable = "循环变量 $i"
        println(loopVariable)
    }
    
    // println(loopVariable)  // 编译错误:无法访问循环变量
}

fun main() {
    demonstrateScope()
}

类成员变量

kotlin
class Person {
    // 实例属性
    var name: String = ""
    val id: String = generateId()
    
    // 私有属性
    private var age: Int = 0
    
    // 保护属性
    protected var email: String = ""
    
    // 内部属性
    internal var department: String = ""
    
    // 延迟初始化属性
    lateinit var address: String
    
    // 懒加载属性
    val expensiveProperty: String by lazy {
        println("计算昂贵属性...")
        "昂贵的计算结果"
    }
    
    companion object {
        // 类属性(静态)
        var totalPersons = 0
        const val SPECIES = "Homo sapiens"
    }
    
    private fun generateId(): String {
        return "PERSON_${System.currentTimeMillis()}"
    }
    
    fun setAge(newAge: Int) {
        if (newAge >= 0) {
            age = newAge
        }
    }
    
    fun getAge() = age
}

fun main() {
    val person = Person()
    person.name = "Alice"
    person.setAge(25)
    person.address = "123 Main St"  // lateinit属性必须在使用前初始化
    
    println("人员信息:")
    println("姓名: ${person.name}")
    println("ID: ${person.id}")
    println("年龄: ${person.getAge()}")
    println("地址: ${person.address}")
    println("昂贵属性: ${person.expensiveProperty}")  // 首次访问时计算
    println("物种: ${Person.SPECIES}")
}

属性访问器

自定义getter和setter

kotlin
class Rectangle(width: Double, height: Double) {
    var width: Double = width
        set(value) {
            if (value > 0) {
                field = value
            } else {
                throw IllegalArgumentException("宽度必须大于0")
            }
        }
    
    var height: Double = height
        set(value) {
            if (value > 0) {
                field = value
            } else {
                throw IllegalArgumentException("高度必须大于0")
            }
        }
    
    // 计算属性(只有getter)
    val area: Double
        get() = width * height
    
    val perimeter: Double
        get() = 2 * (width + height)
    
    // 带backing field的属性
    var name: String = ""
        get() = field.uppercase()
        set(value) {
            field = value.trim()
        }
}

fun main() {
    val rectangle = Rectangle(5.0, 3.0)
    
    println("矩形信息:")
    println("宽度: ${rectangle.width}")
    println("高度: ${rectangle.height}")
    println("面积: ${rectangle.area}")
    println("周长: ${rectangle.perimeter}")
    
    rectangle.name = "  my rectangle  "
    println("名称: '${rectangle.name}'")  // 自动转大写和去空格
    
    // 修改尺寸
    rectangle.width = 6.0
    rectangle.height = 4.0
    println("新面积: ${rectangle.area}")
    
    try {
        rectangle.width = -1.0  // 会抛出异常
    } catch (e: IllegalArgumentException) {
        println("错误: ${e.message}")
    }
}

延迟初始化

lateinit 修饰符

kotlin
class DatabaseService {
    lateinit var connection: String
    
    fun initialize() {
        connection = "数据库连接已建立"
    }
    
    fun isInitialized(): Boolean {
        return ::connection.isInitialized
    }
    
    fun getConnection(): String {
        if (!::connection.isInitialized) {
            throw IllegalStateException("连接未初始化")
        }
        return connection
    }
}

fun main() {
    val service = DatabaseService()
    
    println("初始化状态: ${service.isInitialized()}")
    
    try {
        service.getConnection()  // 会抛出异常
    } catch (e: IllegalStateException) {
        println("错误: ${e.message}")
    }
    
    service.initialize()
    println("初始化后状态: ${service.isInitialized()}")
    println("连接: ${service.getConnection()}")
}

lazy 委托

kotlin
class ExpensiveResource {
    // 懒加载属性
    val expensiveData: List<String> by lazy {
        println("正在加载昂贵的数据...")
        Thread.sleep(1000)  // 模拟耗时操作
        listOf("数据1", "数据2", "数据3")
    }
    
    // 线程安全的懒加载
    val threadSafeData: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
        "线程安全的数据"
    }
    
    // 非线程安全的懒加载(性能更好)
    val nonThreadSafeData: String by lazy(LazyThreadSafetyMode.NONE) {
        "非线程安全的数据"
    }
}

fun main() {
    val resource = ExpensiveResource()
    
    println("资源已创建,但数据尚未加载")
    
    // 首次访问时才会加载数据
    println("首次访问: ${resource.expensiveData}")
    
    // 再次访问使用缓存的值
    println("再次访问: ${resource.expensiveData}")
}

委托属性

标准委托

kotlin
import kotlin.properties.Delegates

class User {
    // 可观察属性
    var name: String by Delegates.observable("初始值") { property, oldValue, newValue ->
        println("${property.name}从'$oldValue'变更为'$newValue'")
    }
    
    // 可否决属性
    var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
        println("尝试将${property.name}从$oldValue变更为$newValue")
        newValue >= 0  // 只允许非负值
    }
    
    // 映射委托
    private val map = mutableMapOf<String, Any?>()
    var email: String by map
    var phone: String by map
}

fun main() {
    val user = User()
    
    println("=== 可观察属性 ===")
    user.name = "Alice"
    user.name = "Bob"
    
    println("\n=== 可否决属性 ===")
    user.age = 25
    user.age = -5  // 会被拒绝
    println("最终年龄: ${user.age}")
    
    println("\n=== 映射委托 ===")
    user.email = "user@example.com"
    user.phone = "123-456-7890"
    println("邮箱: ${user.email}")
    println("电话: ${user.phone}")
}

自定义委托

kotlin
import kotlin.reflect.KProperty

class LoggingDelegate<T>(private var value: T) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        println("获取属性 ${property.name} = $value")
        return value
    }
    
    operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
        println("设置属性 ${property.name} 从 $value$newValue")
        value = newValue
    }
}

class Example {
    var data: String by LoggingDelegate("初始值")
}

fun main() {
    val example = Example()
    
    println("读取数据: ${example.data}")
    example.data = "新值"
    println("再次读取: ${example.data}")
}

变量命名约定

命名规则

kotlin
class NamingConventions {
    // 好的命名示例
    val userName = "john_doe"           // camelCase
    val isActive = true                 // 布尔值用is前缀
    val hasPermission = false           // 布尔值用has前缀
    val canEdit = true                  // 布尔值用can前缀
    
    // 常量命名
    companion object {
        const val MAX_RETRY_COUNT = 3   // UPPER_SNAKE_CASE
        const val API_BASE_URL = "https://api.example.com"
        const val DEFAULT_TIMEOUT = 5000L
    }
    
    // 私有属性
    private val _internalData = mutableListOf<String>()
    val internalData: List<String> get() = _internalData
    
    // 避免的命名
    // val d = "data"              // 太短,不清楚含义
    // val userData123 = "user"    // 避免数字后缀
    // val user_name = "john"      // Kotlin中避免下划线
}

最佳实践

1. 优先使用val

kotlin
fun main() {
    // 好的做法:优先使用val
    val items = mutableListOf<String>()  // 引用不变,但内容可变
    items.add("item1")
    items.add("item2")
    
    // 只在必要时使用var
    var counter = 0
    for (item in items) {
        counter++
        println("$counter: $item")
    }
}

2. 合理使用类型推断

kotlin
fun main() {
    // 好的做法:让编译器推断明显的类型
    val name = "Kotlin"
    val count = 42
    val items = listOf("a", "b", "c")
    
    // 在需要时明确指定类型
    val number: Number = 42  // 需要父类型
    val nullable: String? = getName()  // 可空类型
    val empty: List<String> = emptyList()  // 空集合
}

fun getName(): String? = null

3. 适当使用延迟初始化

kotlin
class Service {
    // 对于昂贵的计算使用lazy
    private val expensiveResource by lazy {
        createExpensiveResource()
    }
    
    // 对于必须在构造后初始化的属性使用lateinit
    lateinit var configuration: Configuration
    
    private fun createExpensiveResource(): String {
        // 模拟昂贵操作
        return "昂贵资源"
    }
}

data class Configuration(val setting: String)

4. 使用有意义的变量名

kotlin
// 好的命名
fun calculateTotalPrice(items: List<Item>, taxRate: Double): Double {
    val subtotal = items.sumOf { it.price }
    val taxAmount = subtotal * taxRate
    val totalPrice = subtotal + taxAmount
    return totalPrice
}

// 避免的命名
fun calc(l: List<Item>, r: Double): Double {
    val s = l.sumOf { it.price }
    val t = s * r
    val total = s + t
    return total
}

data class Item(val price: Double)

下一步

掌握了变量和常量的使用后,让我们学习Kotlin中的各种运算符。

下一章: 运算符

练习题

  1. 创建一个类,展示所有类型的属性声明和访问器
  2. 实现一个配置管理类,使用不同的初始化方式
  3. 设计一个计数器类,使用委托属性来记录访问次数
  4. 编写一个程序来演示变量作用域的规则
  5. 创建一个自定义委托来实现属性的加密存储

本站内容仅供学习和研究使用。