Kotlin 基础语法梳理:变量、控制流程、数组与函数

南乔
南乔
发布时间:2026-08-22| 99人浏览过
原创

前言

Kotlin 的语法表面上比 Java 更简洁,但很多初学者真正卡住的,往往不是“能不能写出来”,而是“这段代码为什么这样设计”。比如变量初始化是在编译期还是运行时检查、区间遍历到底包含哪些边界、顶层函数和扩展函数在 Java 层会变成什么,这些都直接影响你对 Kotlin 的理解。本文按实际使用场景重新整理这些基础语法,并结合原始示例解释它们各自的规则和适用边界。

Kotlin 的基础语法看起来简洁,但真正上手时,很多人会卡在几个细节上:val 和 var 到底怎么选,为什么类型不能隐式转换,for 区间写法该怎么理解,以及顶层函数、扩展函数在 Java 层究竟会变成什么。本文按这些常见问题重新梳理一遍核心语法,并保留关键代码示例,方便你直接对照理解 Kotlin 的实际行为。

读完后,你可以更清楚地判断哪些语法是 Kotlin 的“语法糖”,哪些属于编译期规则,哪些又会影响到与 Java 的互操作方式。这些判断,比单纯记住写法更重要。

变量声明:先理解 val、var 和初始化规则

Kotlin 声明变量主要有两种方式:val 和 var。其中,val 用于声明不可变变量,var 用于声明可变变量。实际开发中,Kotlin 更鼓励优先使用 val,只有在确实需要重新赋值时才改用 var。

Kotlin 变量声明与类型转换规则对照图
Kotlin 变量与类型转换规则用一张图梳理 val、var、初始化检查以及显式类型转换的边界,适合在变量部分后辅助理解。

这里需要注意一个容易被误解的点:val 限制的是“变量引用不能再次指向别处”,并不代表它指向的对象本身一定不可变,这一点与大多数高级编程语言的行为类似。

Kotlin 还提供了较强的类型推断能力,因此声明变量时,通常要么直接给初始值,要么显式写出类型。

val a = 1

1. val b: Int
   b = 2

2. var c = 3
   c = 4
   
3. 
   class cla {
       val d: Int
       init {
           d = 5
       }
   }

编译期初始化检查与 lateinit

如果你声明了类型但没有立刻赋值,Kotlin 会要求该变量在使用前完成初始化,这个检查发生在编译期。

但如果使用 lateinit 关键字声明变量,情况就不同了。它只能用于可变变量,也就是只能修饰 var。此时是否已初始化不再由编译器静态保证,而是推迟到运行时检查。这类写法常见于依赖注入等场景。

另外,成员变量不能像局部变量那样随意先声明再拖到对象创建后赋值。成员属性必须在对象构造完成前初始化,因此常见做法是先声明类型,再在 init 代码块里完成赋值,上面的 d 就属于这种情况。

类型转换:Kotlin 不做隐式转换

Kotlin 的基本数据类型都有类包装能力,因此字面量可以直接调用函数。但它和 Java 一个很大的区别在于:Kotlin 不允许隐式类型转换。

也就是说,Int 不会自动变成 Long。如果要转换,就必须显式调用对应的 toXxx() 方法。不过,字面量本身可以直接调用这些转换函数。

var a: Int = 1
1. var b: Long = a /* x */
2. var b: Long = a.toLong() /* √ */
3. var b: Long = 1.toLong() /* √ */

这条规则的好处是,数值类型变化更明确,阅读和维护时不容易漏掉潜在的精度或范围问题。

控制流程怎么写:循环、区间、if 与 when

Kotlin 的 while 与 Java 基本一致,但 for 更强调“遍历”语义,采用的是 for-in 形式。也就是说,in 前面的变量会依次接收集合或区间中的元素。

Kotlin 区间遍历与表达式控制流示意图
Kotlin 控制流程速览集中说明 for-in、区间写法、step,以及 if/when 作为表达式返回值的特点。

在区间表达上,..、until 和 downTo 是最常见的三种写法:

  • ..:生成全闭区间
  • until:生成左闭右开区间
  • downTo:生成倒序的闭区间

步长默认为 1,也可以在表达式末尾用 step 指定。

while (expression) { ... }

val arr = IntArray(5) /* 创建长度为5的Int数组 */

1. for (num in arr) { ... }

2. for (i in 0..arr.size - 1) { ... }

3. for (i in 0..arr.size - 1 step 2) { ... }

4. for (i in 0 until arr.size) { ... }

5. for (i in arr.size - 1 downTo 0) { ... }

if 和 when 不只是语句,也是表达式

Kotlin 里,if 和对标 Java switch 的 when 都不只是流程控制语句,它们本身还能返回值。返回的是命中分支中最后一行表达式的结果。

这让很多简单判断可以直接写成函数表达式体,代码会更紧凑。

fun max(a: Int, b: Int): Int = if (a > b) a else b

fun is10(c: Int): Boolean = when (c) {
    10 -> true
    else -> fal
} 

从写法上看,这种设计减少了中间变量,也让“条件判断的结果是什么”表达得更直接。

数组怎么区分:Array 和 IntArray 有什么不同

Kotlin 中常见的数组有两类:Array 和 IntArray。它们分别对应 Java 层的 Integer[] 和 int[]。

这不是单纯命名不同,而是底层表示也不同。因此两者互转时,底层逻辑是“拷贝”,并不是零成本视图转换。转换时分别调用 toTypedArray() 和 toIntArray()。

val a = arrayOf(1, 2, 3)
val b = IntArray(5)

val c: Array<Int> = c.toTypedArray()
val d: IntArray = a.toIntArray()

如果你的代码需要频繁在 Kotlin 和 Java 之间传递数组,理解这层差异就很重要,因为它会直接影响性能和 API 选择。

函数不一定属于类:顶层函数与顶层属性

Kotlin 一个很有代表性的设计,就是允许函数直接定义在文件顶层,而不必强行放进某个类里。典型例子就是 main 函数。

Kotlin 顶层函数、扩展函数与 Java 层映射关系图
Kotlin 顶层声明与扩展机制把顶层声明、@file:JvmName、扩展函数和扩展属性在 Java 层的表现放在一张图里。

这种设计很适合工具方法、程序入口以及不依赖对象状态的逻辑。虽然 Kotlin 源码里看不到“工具类”,但在 Java 层,这些顶层函数仍会被包装进自动生成的类中,并以静态方法的形式出现。

fun main() {
    val name = "Kotlin"
println("Hello, " + name + "!")

    for (i in 1..5) {
println("i = $i")
    }
}

顶层函数是以 kt 文件为单位独立存在的。通常情况下,Java 层生成的类名会使用文件名;如果有需要,也可以通过注解自定义这个名字。

@file:JvmName("MathUtils")
fun add(a: Int, b: Int) = a + b

/* -> */

public final class MathUtils {
    /* 没有构造函数 */
    public static int add(int a, int b) {
        return a + b;
    }
}

不过,这里有一个实际开发中的风险点:如果不同文件生成了相同的 Java 类名,就可能出现命名冲突。

顶层属性在 Java 层如何表现

顶层属性的概念与顶层函数类似。它们同样会落到自动生成类中,通常会生成 private static 字段,以及对应的 get/set 方法;如果属性是 val,则没有 set。

这些属性会在对应文件生成类第一次被加载时初始化。

如果你希望某个属性在 Java 中直接表现为 public static final,可以使用 const。但它只能修饰基本数据类型和 String,并且属性本身必须是 val,同时要在声明时完成初始化。

/* God.kt */
val name = "洪秀全"
const val rank = 2

/* -> */

public final class GodKt {
    private static final String name = "洪秀全";
    
    public static getName() {
        return name;
    }
    
    public static final int rank = 2;
}

扩展函数与扩展属性:语法像成员,本质不是改类

很多人会觉得 Kotlin 的标准库 API 比 Java 更“顺手”,其中一个核心原因就是扩展函数。它看起来像给某个类新增了成员方法,但实际上并没有修改原类定义。

从 Kotlin 语法上看,扩展函数写在类外部;从 Java 层看,它本质上仍是静态方法,只不过接收者对象会作为参数传入。

/* StringUtils.kt */
fun String.lastChar(): Char = this.get(this.length - 1)

/* -> */

public final class StringUtilsKt {
    public static char lastChar(String s) {
        return s.charAt(s.length() - 1);
    }
}

/* --- */

class SomeClass {
    fun String.lastChar(): Char = this.get(this.length - 1)
}

/* -> */

public final class SomeClass {
    public static char lastChar(SomeClass sc, String s) {
        return s.charAt(s.length() - 1);
    }
}

扩展函数又可以分为顶层扩展函数和成员扩展函数。两者在 Java 层都还是静态方法,但后者只能在所属类实例的上下文里使用,作用范围更受限制。

这一节也能顺带看出 Kotlin 文件的工程意义:与 Java 必须让文件名对应 public 顶级类不同,Kotlin 文件可以同时包含多个类、多个顶层函数和顶层属性。

扩展属性为什么不能直接存值

扩展属性可以理解为“以属性形式包装的扩展函数返回值”。由于它并没有真的往原类里新增字段,所以扩展属性本身不存在可存储的 backing field。

这意味着它不能直接初始化,也不会自动拥有默认的 get 实现,通常需要自己定义访问器。

val String.lastChar: Char
    get() = get(length - 1)
    
/* -> */

public final class SomeClass {
    public static char getLastChar(String s) {
        return s.charAt(s.length() - 1);
    }
}

可变参数与中缀调用:mapOf 背后做了什么

Kotlin 支持用 vararg 声明可变参数,这一点和 Java 类似,但使用上有一个关键差别:Java 的可变参数函数通常可以直接传数组,而 Kotlin 需要显式展开数组,写法是使用展开运算符 *。

这个特性在 mapOf、listOf 等标准库函数里非常常见。

/* mapOf的函数声明 */
1. public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V>

2. val map = mapOf(1 to "one", 2 to "two", 3 to "three")

3. 1.to("one")

4. infix fun Any.to(other: Any) = Pair(this, other)

5. 
fun main(args: Array<String>) {
    val list = listOf("args: ", *args)
    /* 相当于用"args: "和args的各参数建立集合 */
}

上面这段代码还涉及另一个 Kotlin 很有辨识度的语法:中缀调用。像 1 to "one" 这种写法,本质上是函数调用的简化形式,对应普通写法就是 1.to("one")。

要声明一个中缀函数,需要满足“只有一个参数”,并使用 infix 修饰。标准库里的 to 正是一个扩展函数,因此它可以方便地创建任意类型的 Pair。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。