1. 概述

在本文中,我们将探讨一些常见的 Kotlin 面试问题,按照初级和高级两个层次进行分类,便于理解和准备。

这些问题涵盖了 Kotlin 的核心语法、语言特性、与 Java 的互操作性,以及一些高级概念如协程、高阶函数等。对于准备 Kotlin 相关岗位面试的开发者来说,是一份不错的参考资料。


2. 初级问题

2.1 什么是 Kotlin?它与 Java 如何互操作?

Kotlin 是由 JetBrains 开发的基于 JVM 的通用编程语言。它是一种静态类型语言,支持过程式、函数式和面向对象编程。

Kotlin 与 Java 的互操作性非常强,因为 Kotlin 编译器生成的字节码与 Java 编译器生成的字节码完全兼容。这意味着你可以在一个项目中同时使用 Kotlin 和 Java,相互调用类和方法。

此外,Kotlin 提供了一些实用特性,如范围表达式、扩展函数、空安全、智能类型转换等,使其成为 Java 的一个更现代化替代方案。

Google 官方推荐 Kotlin 作为 Android 开发的首选语言


2.2 Kotlin 中变量声明的类型有哪些?

Kotlin 中使用 valvar 关键字声明变量:

val userName: String = "John"
var age = 25
  • val:不可变变量(只读),只能赋值一次。
  • var:可变变量,可以多次赋值。

如果没有指定类型,Kotlin 会通过类型推断自动识别变量类型。

另外,还可以使用 const 关键字配合 val 声明编译时常量。


2.3 valconst 有什么区别?

  • val:运行时不可变变量,可以在运行时初始化,例如通过函数调用赋值。
  • const:编译时常量,必须在声明时赋值,且值必须是基本类型或字符串。

示例:

val welcomeMsg: String = "Hello $userName"
const val FILE_EXTENSION: String = ".jpg"

⚠️ const 只能用于 val,且不能赋值为函数调用或构造函数。


2.4 如何在 Kotlin 中创建构造函数?

Kotlin 支持两种构造函数:

  • 主构造函数(Primary Constructor):定义在类头中,通常用于初始化属性。
  • 次构造函数(Secondary Constructor):定义在类体内,用于提供额外的初始化逻辑。

主构造函数示例:

class Person(var firstName: String, var lastName: String) {
    var age: Int = 0
}

val person = Person("John", "Smith")

次构造函数示例:

class Person(var firstName: String, var lastName: String) {
    var age: Int = 0

    constructor(firstName: String, age: Int) {
        this.firstName = firstName
        this.age = age
    }

    constructor(firstName: String, lastName: String, age: Int): this(firstName, lastName) {
        this.age = age
    }
}

2.5 Kotlin 中的字符串插值是什么?

Kotlin 支持两种字符串插值方式:

  • $变量名:插入变量。
  • ${表达式}:插入表达式结果。

示例:

val name = "John"
val greeting = "Hello $name"  // Hello John

val seconds = "${60 * 60 * 24 * 365} seconds"  // 31536000 seconds

2.6 Kotlin 中的空安全机制是什么?

Kotlin 默认不允许变量为 null,以避免 NullPointerException

如果需要允许变量为空,可以使用 ? 声明可空类型:

var name: String? = null

Kotlin 提供了多种空处理操作符:

  • ?.:安全调用。
  • ?::Elvis 运算符,提供默认值。
  • !!:非空断言,若为 null 抛出异常。

2.7 Kotlin 中的 data class 是什么?

data class 是一种简化 POJO(普通 Java 对象)定义的类,自动实现 equals()hashCode()toString() 等方法。

只需添加 data 关键字即可:

data class Person(var firstName: String, var lastName: String)

✅ data class 还会自动生成 copy()componentN() 等方法。


2.8 如何在 Kotlin 中创建单例类?

Kotlin 提供了 object 关键字来实现单例模式:

object SimpleSingleton

该方式在编译时自动实现线程安全的懒加载机制。


2.9 Kotlin 中的双感叹号(!!)操作符是什么?

!! 是非空断言操作符。如果变量为 null,它会抛出 NullPointerException

适用于你确定变量不会为 null 的场景:

val name: String? = getName()
val length = name!!.length  // 若 name 为 null,抛异常

⚠️ 使用时要小心,避免运行时崩溃。


2.10 Kotlin 中的 range 表达式是什么?

range 表达式用于表示一个值的范围,支持迭代和判断值是否在范围内。

Kotlin 提供了多种写法:

(1..10)             // 从 1 到 10
(1.rangeTo(10))     // 同上
(10.downTo(1))      // 从 10 到 1
('a'..'z')          // 从 a 到 z
(1..10 step 2)      // 步长为 2 的范围

还可以使用 untilreversefirstlast 等方法进行操作。


2.11 Kotlin 中的 when 是什么?

when 是 Kotlin 中的条件判断语句,类似于 Java 的 switch-case,但功能更强大。

示例:

when (person.firstName) {
    "Mike" -> println("hello!")
    "John" -> println("howdy!")
    else -> println("hi!")
}

✅ Kotlin 的 when 支持表达式、类型检查、范围匹配等多种用法,且不需要 break


2.12 Kotlin 中有哪些访问修饰符?默认是哪个?

Kotlin 支持以下访问修饰符:

修饰符 作用范围
public 默认修饰符,全局可见
private 当前文件或类内可见
protected 当前类及其子类可见
internal 同一模块内可见

⚠️ Kotlin 没有 Java 的 default 包访问权限,而是使用 internal


2.13 Kotlin 中的 open 是什么?

Kotlin 默认所有类和方法都是 final(不可继承/重写),若希望允许继承或重写,需要使用 open 关键字。

例如:

open class Animal {
    open fun speak() {
        println("Animal speaks")
    }
}

open 是 Java 中 final 的反义词。


3. 高级问题

3.1 Kotlin 相比 Java 有哪些优势?

Kotlin 作为 Java 的现代化替代语言,具有以下优势:

  • ✅ 更简洁的语法,减少样板代码。
  • ✅ 空安全机制,减少 NPE。
  • ✅ 扩展函数,无需继承或装饰器。
  • ✅ 范围表达式、字符串插值等语法糖。
  • ✅ 协程支持,简化异步编程。
  • ✅ 支持函数式编程特性(高阶函数、lambda 表达式)。
  • ✅ 与 Java 完全互操作。
  • ✅ Google 官方推荐用于 Android 开发。

3.2 inline 和 infix 函数分别是什么?

inline 函数

inline 函数在编译时将函数体直接插入调用处,避免创建额外的 lambda 对象,提升性能。

示例:

inline fun isEven(number: Int): Boolean {
    return number % 2 == 0
}

val resultList = (1..10).filter { isEven(it) }

✅ 适用于频繁调用的 lambda 函数。

infix 函数

infix 函数允许使用中缀表达式调用,使代码更接近自然语言。

示例:

infix fun Int.times(value: String): String {
    return value.repeat(this)
}

val result = 3 times "hello"  // "hellohellohello"

✅ 常见的 infix 函数有 toandor 等。


3.3 lazy 和 lateinit 有什么区别?

特性 lazy lateinit
类型 val var
初始化方式 Lambda 表达式 可在任意地方赋值
是否允许 null 否(未初始化会抛异常)
多次赋值
支持类型 所有类型 不支持基本类型

示例:

val lazyValue: String by lazy {
    "Initialized"
}

lateinit var name: String
name = "John"

3.4 fold 和 reduce 函数的区别是什么?

两者都用于集合的折叠操作,区别在于是否提供初始值。

  • fold:提供初始值,从初始值开始累加。
  • reduce:不提供初始值,从集合第一个元素开始。

示例:

val sum1 = (1..10).fold(10) { acc, i -> acc + i }  // 65
val sum2 = (1..10).reduce { acc, i -> acc + i }    // 55

fold 支持更改结果类型(通过初始值类型)。


3.5 Kotlin 中的协程是什么?

协程是一种轻量级的并发编程方式,允许异步、非阻塞地执行任务。

Kotlin 提供了 kotlinx-coroutines 库,支持:

  • 挂起函数(suspend
  • 协程构建器(launchasync
  • 作用域(CoroutineScope
  • 调度器(Dispatchers

示例:

GlobalScope.launch {
    val result = async { fetchData() }.await()
    println(result)
}

✅ 协程比线程更高效,适合大量并发任务。


3.6 Kotlin 中的 companion object 是什么?

Kotlin 没有静态成员,但可以通过 companion object 实现类似静态方法的调用。

示例:

class Person(val name: String) {
    companion object {
        val defaultPerson = Person("Mike")
    }
}

println(Person.defaultPerson.name)  // Mike

companion object 是一个单例对象,可以拥有名称,也可以实现接口。


3.7 Kotlin 中的位运算符有哪些?

Kotlin 支持常见的位运算符,但命名更直观:

Java 运算符 Kotlin 运算符
` `
& and
^ xor
~ inv()

位移操作符:

  • shl:左移
  • shr:带符号右移
  • ushr:无符号右移

示例:

assert(3 shl 3 == 24)
assert(12 shr 2 == 3)
assert(-1 ushr 1 == Int.MAX_VALUE)

4. 总结

本文整理了 Kotlin 面试中常见的初级和高级问题,覆盖了语言特性、语法糖、面向对象、函数式编程、并发等多个方面。

这些问题虽然基础,但往往能体现开发者对 Kotlin 的掌握深度。建议在理解的基础上多做实践,才能在面试中游刃有余。

进阶建议:深入理解协程、高阶函数、DSL、泛型系统等高级特性,对 Android 开发者尤为重要。


原始标题:Kotlin Interview Questions