理解一门语言的最好方式,是用它写代码。不是读关于它的文章,不是看网络讲座,不是把文章收藏进"待读"清单(我们都知道那些文章最后去哪了)。 Kotlin 从零开始:入门教程 - 1打开 IDE,写点真正能跑起来的东西。

这篇教程就是为此而写的。我们会从零出发,完成第一个可运行的迷你应用。如果你懂 Java,进度会更快——我会全程做对比。

运行 Kotlin:两种方式,其中一种零分钟上手

直接在浏览器里。打开 play.kotlinlang.org,直接开写。不用安装,不用配置,不用钻进"为什么环境搭不起来"的技术论坛死胡同。最适合第一次接触。

在 IntelliJ IDEA 里。对 Kotlin 的支持是内置的。新建一个 Kotlin 项目——就这么简单。不用装插件,没有任何繁琐流程。

你的第一个程序:

fun main() {
    println("Hello, Kotlin!")
}

Java 里同样的代码是这样的:

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, Kotlin!");
    }
}

两者做的事情完全一样。Kotlin 只是没有 public static void 和包裹类——因为……我们当初为什么需要那些东西?好问题。Java 从来没给出让人信服的答案。

val 和 var:两个需要记住的关键字

在 Kotlin 里,变量用 valvar 声明。

val name = "伟伟"   // 不可变。相当于 Java 里的 final。
var age = 25        // 可变。

age = 26            // 没问题。
name = "娜娜"       // 编译错误。

简单原则:凡是不需要改变的值,一律用 val。只有真正需要修改的时候才用 var。这就像提前和自己约定:"这个我不会动。" Kotlin 会确保你信守承诺。

类型不用写——Kotlin 自己推断:

val score = 100     // Kotlin 知道:这是 Int
val name = "伟伟"   // 这是 String

或者显式写出来,如果你更喜欢这样:

val score: Int = 100
val name: String = "伟伟"

Null-safety:Kotlin 如何消灭 NullPointerException

Tony Hoare 在 1965 年发明了 null,后来把它称为自己"价值十亿美元的错误"。听起来像自我批评——但其实是低估了。NullPointerException 给这个行业造成的损失远不止这个数。

在 Java 里,任何变量都可能是 null。编译器沉默。你在运行时才知道。在生产环境里。有时候是在给客户做现场演示的时候——对,这种事真的发生过。

Kotlin 在类型系统层面解决了这个问题:

var name: String = "伟伟"
name = null  // 编译错误。立刻报错。在运行前就拦截了。

如果一个变量可能是 null,必须用 ? 明确声明:

var name: String? = "伟伟"
name = null  // 我们约定好它可以为 null — 现在没问题。

那么当我们需要在可能为 null 的东西上调用方法时怎么办?

println(name?.length)        // 如果是 null — 返回 null。不会崩。
println(name?.length ?: 0)   // 如果是 null — 返回 0。Elvis 运算符。

它叫 Elvis 运算符——因为 ?: 看起来像猫王的发型。这不是玩笑,这是官方文档里的正式说明。

函数

Java 里:

public static int add(int a, int b) {
    return a + b;
}

Kotlin 里:

fun add(a: Int, b: Int): Int {
    return a + b
}

更简洁的写法——单表达式函数:

fun add(a: Int, b: Int) = a + b

Kotlin 支持默认参数值。Java 里要为此写好几个重载方法。Kotlin 只需要一个:

fun greet(name: String, greeting: String = "你好") {
    println("$greeting,$name!")
}

greet("伟伟")           // 你好,伟伟!
greet("娜娜", "嘿")     // 嘿,娜娜!

字符串模板——直接在字符串里用 $name。Kotlin 自动把变量值替换进去。不用 +,不用拼接——就应该是这样的嘛。

Data class:告别 boilerplate

经典任务:需要一个数据模型——一个有姓名、年龄和邮箱的用户。

在 Java 里,这意味着构造函数、三个 getter、三个 setter、equals()hashCode()toString()。或者用 Lombok。或者接受现实,像 2005 年那样手写一切。

在 Kotlin 里:

data class User(val name: String, val age: Int, val email: String)

一行。编译器生成其他所有内容,包括 copy()

val weiwei = User("伟伟", 30, "weiwei@example.com")
val olderWeiwei = weiwei.copy(age = 31)

println(weiwei)
// User(name=伟伟, age=30, email=weiwei@example.com)

看看 copy()。你创建一个新对象,只改需要改的字段。Java 里通常要为此写一个 Builder。或者直接忍着。

Extension functions:给别人的类加方法

假设你想给 String 类加一个方法。Java 里——写一个带静态方法的工具类,然后到处写 StringUtils.doSomething(myString)。能用,但感觉像打了个补丁。

Kotlin 里——extension functions:

fun String.isValidEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

println("user@example.com".isValidEmail())  // true
println("这不是邮箱".isValidEmail())         // false

读起来就像这个类原生就有这个方法。没有继承,没有包装器。就是加了个方法——然后直接用。

整合起来:一个用户验证器

把我们学到的东西合起来,写点真实的代码。

data class User(val name: String, val email: String?)

fun String.isValidEmail() = contains("@") && contains(".")

fun validateUser(user: User): String {
    val emailStatus = user.email?.let {
        if (it.isValidEmail()) "有效" else "无效"
    } ?: "未填写"

    return "${user.name}:邮箱$emailStatus"
}

fun main() {
    val users = listOf(
        User("伟伟", "weiwei@example.com"),
        User("娜娜", "nana-not-email"),
        User("小明", null)
    )
    users.forEach { println(validateUser(it)) }
}

输出:

伟伟:邮箱有效
娜娜:邮箱无效
小明:邮箱未填写

注意:null 的处理没有用一个 if?.?: 运算符优雅地搞定了一切。小明没填邮箱——没有任何 NPE。生活美好。

这只是个开始

这里我们覆盖了基础:变量、null-safety、函数、data class、extension functions。

后面还有更有趣的内容。协程——无痛的异步代码。Sealed classes——比 switch 更强大的替代方案,不会让你漏掉任何情况。集合的函数式操作——mapfilterreduce 一行搞定。

如果你想系统地学 Kotlin,而不是从散落在各个浏览器标签页的碎片里拼凑——来试试我们的课程吧。62 个关卡,1100 多道题含自动验证,3 个项目可加入作品集。第一关免费。

codegym.cc/zh/courses/kotlin

继续阅读