【Kotlin系统化精讲:叁】 | 变量与常量:自由与约束的代码博弈

前言

变量常量 就像生活中的水杯玻璃雕塑 ​:水杯能随时装不同饮品(变量 ),玻璃雕塑一旦定型就永恒不变(常量 )。但 valconst val 藏着意想不到的玄机------有些"常量"竟会偷偷变化?

本章节将带你捅破变量与常量的窗户纸,避开新手90%的踩坑点。

千曲 而后晓声,观千剑 而后识器。虐它千百遍 方能通晓其真意


变量:var & val 🎭

var: 可变变量

  • 核心特性 :变量可多次重新赋值 ,赋予数据 二次生命 的能力。

    Kotlin 复制代码
    var score = 90  
    score = 95  // 分数重生 → ✅   
  • 声明及初始化 :类型一旦确定后不可修改!隐式声明时取决于第一次赋值的类型

    Kotlin 复制代码
    var age: Int = 25  // 显式声明
    var age = 18 // 隐式声明
    age = "二十六"  // ❌ 编译报错!类型已锁定为 Int  

    💡 ​破解之道 ​:可声明为Any类型,但需警惕类型安全风险。


val:只读变量(运行时常量

  • 核心特性(不可变性):只能赋值一次,不可重新赋值 。 官方推荐的默认选择。尽可能地使用 val 可避免意外的状态改变,提高代码的可预测性线程安全性

  • 声明及初始化 :声明时必须初始化,或在类构造函数中初始化,或使用 lateinitby lazy 等委托进行延迟初始化。初始化后,其引用 (对于对象类型)或 (对于基本类型)就不能再被改变。

  • 颠覆认知(对象内容可篡改 :只读内容不可变!

    Kotlin 复制代码
    val androidVersions = arrayListOf("Oreo", "Pie")  
    androidVersions.add("Q")  // ✅ 集合内容被修改!  
    androidVersions = newList  // ❌ 引用不可变 

    🧩 ​本质原因 ​:val 只保证引用地址不变​(购物车还是那辆车),但车里的货物(对象内容)随便改!

  • 智能转换魔法 :类型推断升级

    Kotlin 复制代码
    val data: Any = "Kotlin"  
    if (data is String) {  
        print(data.length)  // ✅ 编译器自动转为 String 类型  
    }  

    编译器秘密:在作用域内自动插入类型转换字节码。

  • 延迟初始化lateinit var 的替代方案

    Kotlin 复制代码
    val config: Configuration by lazy {  
        loadConfigFromServer()  // 首次访问时初始化  
    }  

    🌟 优势:线程安全 + 按需加载 + 不可变引用


常量:const val(编译期常量) ⏳

const val:真正的化石

核心目的 是声明编译期常量。 想要彻底不可变 ​?用 const val

Kotlin 复制代码
// 必须定义在顶层或 object 中  
const val PI = 3.14159  
const val COMPANY_NAME = "AndroidLab"

class MyClass {
    companion object { 
        const val API_KEY = "ABCDEF123456" // 注意:实际API密钥应安全存储,这仅为示例 
        const val VERSION_CODE = 1 
    } 
}

特性​:

  • 必须是顶层属性object 声明的成员。
  • 必须在编译时就能确定值及其类型
  • 只能是基本类型String
  • 不能有自定义的 getter

编译期优化:

编译器会在编译期间直接用字面值替换所有使用该常量的地方 ,类似于宏替换(没有运行时的变量读取开销)。


val vs const val:生死对决

特性 val const val (编译时常量)
关键特性 运行时只读变量,引用不可变 编译期常量,值内联
声明位置 任何作用域(函数内、类属性等) 仅顶层或 object(含伴生对象)内
允许的类型 所有类型 仅基本类型 (Int等) 和 String
初始化要求 运行时初始化 编译时初始化 ​(字面量/其他const val)
自定义 getter 可以有 不能有
使用 lateinit 可以 不能
by lazy 可以 不能
内存开销 运行时存在变量 编译后替换为字面量,无运行时变量
主要用途 表示程序运行期间不变的引用 定义字面值的命名常量,用于注解参数等

🌰 举个栗子​:

Kotlin 复制代码
val currentTime: Long  
    get() = System.currentTimeMillis() // 每次调用值都变!  

const val MAX_USERS = 1000 // 恒等于 1000  

深入特性:藏在代码缝里的魔鬼 😈

类型推断的"障眼法"

Kotlin 能自动推断类型,但玩过头会翻车:

Kotlin 复制代码
val number = 100   // 自动推断为 Int  
number = 10.5      // ❌ 拒绝!Int 怎能变 Double?  

急救方案​:显式声明类型避免误伤:

Kotlin 复制代码
val number: Number = 100  // 声明为父类  
number = 10.5             // ✅ 此时合法!  

幕后字段的"分身术"

自定义 getter/setter 时,偷偷生成幕后字段 ​(field):

Kotlin 复制代码
var name: String = ""  
    set(value) {  
        field = value.trim() // field 是隐藏的存储变量  
    }  

企业级实践:老司机的求生法则 🚀

  • 优先 val :
    除非明确需要修改,否则全用 val------减少意外篡改(官方推荐)。

  • const val 使用场景

    • 魔法数字(如超时时间、错误码)。
    • 全局配置(API 密钥、常量字符串)。
    Kotlin 复制代码
    const val API_TIMEOUT = 30_000  
  • 警惕可变 val!​
    如果 val 指向可变对象(如 ArrayList),加防御性拷贝

    Kotlin 复制代码
    val safeList = Collections.unmodifiableList(rawList)  
  • 变量命名防混淆

    • 变量 :驼峰命名(userName)。
    • 常量 :全大写+下划线(USER_LIMIT)。

总结

Kotlinvarval 编织了一张精妙的安全网🕸️------var 给灵活留了门,val 用引用不可变保护了基础安全,而 const val 才是真正的"代码化石"val 不等于内容不可变 ,遇到集合需警惕;const val 才是编译期铁律 ,零开销且绝对静止。掌握这三者,你的 Kotlin 代码将兼具弹性与稳定。✨

欢迎一键四连关注 + 点赞 + 收藏 + 评论

相关推荐
M00668810 分钟前
低代码系统的技术深度:超越“可视化操作”的架构与实现挑战
android·rxjava
whysqwhw14 分钟前
Activity 的启动模式|Flags|IntentFilter
android
whysqwhw1 小时前
安卓Hook系统服务实现插件化的核心原理
android
whysqwhw1 小时前
JVM与安卓ClassLoader对比
android
华科云商xiao徐2 小时前
Kotlin动态代理池+无头浏览器协程化实战
爬虫·tcp/ip·kotlin
深盾安全2 小时前
聊聊 Android 软件破解那些事
android
智江鹏3 小时前
Android 之 Kotlin中的符号
android·开发语言·kotlin
ilmari4 小时前
HarmonyOS 基于Network Kit封装的网络请求工具
android·flutter·harmonyos
jzlhll1234 小时前
kotlin协程2025 通俗易懂三部曲之下篇 异常处理
kotlin·协程