第3章:Kotlin基础语法(一):变量声明(val vs var)、类型推断、基本数据类型、字符串模板

好,咱们正式开始写Kotlin代码了。

说实话,从Java转到Kotlin,第一个让我觉得「哇,真香」的地方,就是变量声明。Java里写个变量,你得先想好类型,再写变量名,再赋值。Kotlin呢?一句话就能搞定,而且更安全。

这一章,咱们就把变量声明、类型推断、基本数据类型和字符串模板这几个最基础的东西讲透。别小看这些基础,我见过不少老Android开发,写Kotlin半年了,还在用Java的思维写val和var,结果踩了不少坑。

3.1 val vs var:不可变优先

先看个对比,你感受一下:

// Java
final String name = "张三";
String age = 18;

// Kotlin
val name = "张三"
var age = 18

val 对应 Java 的 final,表示只读变量,赋值后不能改。var 就是普通可变变量。

我个人习惯是:能用val就不用var。为什么?

  • val 让代码更安全,别人不会意外修改你的变量
  • val 更容易推理,看到 val 就知道这个值不会变
  • val 在多线程环境下更友好
我的经验:在项目中,我几乎90%的变量都用val声明。只有那些确实需要改变的,比如循环计数器、状态标志位,才用var。你想想看,如果一个变量从头到尾都不变,你为什么要给它改的机会?

但要注意,val 不等于 Java 的 final 那么简单。val 指向的对象内部状态是可以变的:

val list = mutableListOf("A", "B")
list.add("C")  // 可以!list指向没变,但内容变了
// list = mutableListOf("D")  // 编译错误!不能重新赋值

嗯,这里要注意:val 保证的是引用不可变,不是对象不可变。如果你想要完全不可变,得用不可变集合或者数据类配合 copy()。

3.2 类型推断:少写一半代码

Kotlin 有强大的类型推断能力。说白了,编译器能根据右边的值,自动推断出变量的类型。

val name = "张三"        // 推断为 String
val age = 18             // 推断为 Int
val height = 1.75        // 推断为 Double
val isStudent = true     // 推断为 Boolean

这比Java省事多了。Java里你得写:

String name = "张三";
int age = 18;

Kotlin 把左边的类型声明给省了。但注意,类型推断不是万能的。有些情况你还是得显式声明类型:

  • 函数返回值类型模糊时
  • 你想让变量是父类型而不是子类型时
  • 递归函数

避坑指南:我曾经在项目里遇到过一个bug,就是因为类型推断推错了。当时我写了个 val result = someFunction(),结果someFunction返回的是 Any?,我以为是String,直接调用了length,编译没过。后来我养成了习惯:对于复杂表达式,显式声明类型更安全。

你也可以手动指定类型,虽然不常用:

val name: String = "张三"
val age: Int = 18

我个人建议:简单变量用推断,复杂表达式或者公共API的返回值,显式声明类型更好。

3.3 基本数据类型:一切皆对象

Java 里基本类型和引用类型是分开的,int、boolean 这些不是对象。Kotlin 不一样,一切皆对象

Kotlin 的基本数据类型包括:

类型 说明 Java对应
Int 整型,32位 int / Integer
Long 长整型,64位 long / Long
Float 单精度浮点,32位 float / Float
Double 双精度浮点,64位 double / Double
Boolean 布尔值 boolean / Boolean
Char 单个字符 char / Character
String 字符串 String

在Kotlin里,这些类型都是类,你可以调用它们的方法:

val num = 42
println(num.toString())   // "42"
println(num.toLong())     // 42L
println(num.rangeTo(50))  // 42..50

你想想看,Java里你能对int直接调用方法吗?不行,得先装箱成Integer。Kotlin 帮你省掉了这个麻烦。

注意:Kotlin 在编译时会尽量优化,把对象类型映射为Java的基本类型,避免装箱开销。但如果你用了可空类型(比如 Int?),那就必须装箱了。所以,能用非空类型就用非空类型,性能更好。

数字字面量还有一些小技巧:

val oneMillion = 1_000_000   // 下划线分隔,方便阅读
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010  // 二进制字面量

嗯,这个下划线分隔,我在写金额、ID的时候特别喜欢用,一眼就能看出位数。

3.4 字符串模板:告别拼接噩梦

Java里拼接字符串有多痛苦?

String name = "张三";
int age = 18;
String message = "我叫" + name + ",今年" + age + "岁。";

写多了手都酸,而且容易漏掉加号或者引号。Kotlin 的字符串模板直接解决了这个问题:

val name = "张三"
val age = 18
val message = "我叫$name,今年${age}岁。"

$ 加变量名,就能直接把变量嵌入到字符串里。如果变量后面紧跟其他字符,或者要调用方法,就用 ${} 包起来。

更复杂的表达式也可以:

val price = 19.99
val count = 3
val total = "总价:${price * count} 元"  // 输出:总价:59.97 元

甚至可以在模板里调用方法:

val name = "zhang san"
val upperName = "大写:${name.uppercase()}"  // 输出:大写:ZHANG SAN
我的习惯:在项目中,我几乎不用字符串拼接了。字符串模板不仅简洁,而且可读性高。特别是写日志的时候,一行模板就能把多个变量清晰地展示出来。比如:Log.d(TAG, "用户$userId 在${formatDate(time)} 执行了操作"),比用加号拼接舒服多了。

另外,Kotlin 还支持原始字符串,用三个引号 """ 包裹,可以保留换行和缩进:

val json = """
    {
        "name": "张三",
        "age": 18
    }
""".trimIndent()  // 去掉前导空格

这个在写多行文本、JSON、SQL 的时候特别有用。我曾经在项目里用原始字符串写了一段复杂的SQL查询,同事看了都说清晰。

小结

这一章的内容其实不多,但都是日常写Kotlin的基础。总结一下:

  • val vs var:优先用val,只有需要改变时才用var
  • 类型推断:能省则省,但复杂场景显式声明更安全
  • 基本数据类型:一切皆对象,但注意可空类型会装箱
  • 字符串模板:用$和${},告别拼接噩梦

下一章,咱们会继续深入Kotlin的函数和条件控制。到时候你会发现,Kotlin 在流程控制上也有很多让人眼前一亮的设计。准备好了吗?