Kotlin 中的常量:别再猜测,开始正确使用
\n\n如果你使用 Kotlin 编程至少一周,你很可能已经遇到过这个问题:“如何正确声明一个常量?” 看起来一切都很简单——有 val,有 const val,还有 companion object。但魔鬼,一如既往,藏在细节中。而 Stack Overflow 上的这个问题积累了数千次浏览,并非无缘无故。
让我们一劳永逸地弄清楚 Kotlin 中存在哪些创建常量的方法,何时使用哪种方法,以及为什么 val 并不总是常量。开始吧!
1. val vs const val:陷阱在哪里?
\n\n新手最常见的问题是:“我写了 val——这算是常量吗?” 答案是:既是也不是。
val 是一个不可变引用(只读)。但它的值是在运行时计算的。也就是说,它不是编译时常量。
// 这不是编译时常量\nval currentTime = System.currentTimeMillis()\n\nconst val 是一个真正的常量,它在编译阶段就被嵌入到代码中(就像 Java 中的 static final)。
// 而这是一个常量\nconst val PI = 3.14159\n\n重要限制: const val 只能用于基本类型和 String。不能用于对象、列表或自定义类。
💡 黄金法则: 如果值在编译时已知——使用 const val。如果在运行时计算——使用 val。
2. 在哪里声明常量?前 3 种方法
\n\n方法 1:顶层常量(推荐)
\n\n最简单、最清晰的方法——在文件的顶层声明常量。不需要类,不需要对象——只是一个包含常量的文件。
\n\n// Constants.kt\nconst val BASE_URL = "https://api.example.com"\nconst val TIMEOUT_SECONDS = 30\nconst val APP_NAME = "MyKotlinApp"\n\n为什么这很棒?
\n- \n
- 代码最少 \n
- 无需前缀即可随处访问 \n
- 编译器内联值 \n
- 易于重构 \n
方法 2:类内部的 companion object
\n\n来自 Java 的经典方法,已移植到 Kotlin。当常量在逻辑上与类相关时使用。
\n\nclass NetworkClient {\n companion object {\n const val DEFAULT_PORT = 8080\n const val PROTOCOL = "https"\n }\n}\n\n// 使用\nval port = NetworkClient.DEFAULT_PORT\n\n缺点:语法噪音。优点:常量与上下文绑定。
\n\n方法 3:用于常量组的 object(单例)
\n\n当常量很多且需要按意义分组时使用。
\n\nobject AppConstants {\n const val MAX_LOGIN_ATTEMPTS = 5\n const val SESSION_DURATION = 3600L\n const val DATE_FORMAT = "yyyy-MM-dd"\n\n object Urls {\n const val LOGIN = "/auth/login"\n const val LOGOUT = "/auth/logout"\n }\n}\n\n// 使用\nAppConstants.Urls.LOGIN\n\n3. Android 中的常量:特点
\n\n如果你为 Android 编写代码,有几个额外的细微差别。
\n\ncompanion object 中的常量和 @JvmStatic
\n\n为了使 companion object 中的常量成为 Java 中真正的静态常量(例如,用于注解),请使用 @JvmStatic:
class MyFragment : Fragment() {\n companion object {\n @JvmStatic\n const val ARG_ID = "id"\n }\n}\n\n资源中的常量
\n\n不要忘记经典的 Android 方式——资源。用于字符串、颜色、尺寸:
\n\n// strings.xml\n<string name="app_name">MyApp</string>\n\n// 在代码中\nval appName = getString(R.string.app_name)\n\n但这些不是编译时常量。