变量和常量
概述
本章详细介绍Kotlin中变量和常量的声明、初始化和使用。Kotlin通过var和val关键字来区分可变和不可变的变量,这是函数式编程和不可变性原则的体现。
变量声明
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? = null3. 适当使用延迟初始化
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中的各种运算符。
下一章: 运算符
练习题
- 创建一个类,展示所有类型的属性声明和访问器
- 实现一个配置管理类,使用不同的初始化方式
- 设计一个计数器类,使用委托属性来记录访问次数
- 编写一个程序来演示变量作用域的规则
- 创建一个自定义委托来实现属性的加密存储