# 01 | Kotlin基础语法:正式开启学习之旅 你好,我是朱涛。从今天开始,我们就正式踏上Kotlin语言学习与实践的旅途了。这节课,我想先带你来学习下Kotlin的基础语法,包括变量、基础类型、函数和流程控制。这些基础语法是程序最基本的元素。 不过,如果你有使用Java的经验,可能会觉得今天的内容有点多余,毕竟Kotlin和Java的基础语法是比较相似的,它们都是基于JVM的语言。但其实不然,Kotlin作为一门新的语言,它包含了许多新的特性,由此也决定着Kotlin的代码风格。**如果你不够了解Kotlin的这些新特性,你会发现自己只是换了种方式在写Java而已。** 并且,在具备Java语言的知识基础上,这节课的内容也可以帮你快速将已有的经验迁移过来。这样的话,针对相似的语法,你可以直接建立Kotlin与Java的对应关系,进而加深理解。当然,即使你没有其他编程经验也没关系,从头学即可,Kotlin的语法足够简洁,也非常适合作为第一门计算机语言来学习。 在课程中,我会用最通俗易懂的语言,来给你解释Kotlin的基础知识,并且会结合一些Java和Kotlin的代码案例,来帮助你直观地体会两种语言的异同点。而针对新的语法,我也会详细解释它存在的意义,以及都填补了Java的哪些短板,让你可以对Kotlin新语法的使用场景做到心中基本有数。 ## 开发环境 在正式开始学习基础语法之前,我们还需要配置一下Kotlin语言的环境,因为直接从代码开始学能给我们带来最直观的体验。 那么要运行Kotlin代码,最快的方式,就是**使用Kotlin官方的**[PlayGround](https://play.kotlinlang.org/)。通过这个在线工具,我们可以非常方便地运行Kotlin代码片段。当然,这种方式用来临时测试一小段代码是没有问题的,但对于复杂的工程就有些力不从心了。 另一种方式,也是**我个人比较推荐的方式,那就是安装**[IntelliJ IDEA](https://www.jetbrains.com/idea/download/)。它是Kotlin官方提供的集成开发工具,也是世界上最好的IDE之一,如果你用过Android Studio,你一定会对它很熟悉,因为Android Studio就是由IntelliJ IDEA改造的。 如果你的电脑没有Java环境,在安装完最新版的IntelliJ IDEA以后,通过“File -> Project Structure -> SDKs”,然后点击“加号按钮”就可以选择第三方提供的OpenJDK 1.8版本进行下载了。 ![图片](https://static001.geekbang.org/resource/image/04/a7/04cf1b899574ceff2ecd099e41af1fa7.gif?wh=1000x770) 当然,这里我更推荐你可以自己手动从[Oracle官网](https://www.oracle.com/java/technologies/downloads/)下载JDK 1.6、1.7、1.8、11这几个版本,然后再安装、配置Java多版本环境。这在实际工作中也是必备的。 需要注意的是,IntelliJ IDEA分为Ultimate付费版和Community免费版,对于我们的Kotlin学习来说,免费版完全够用。 这样,在配置好了开发环境之后,我们就可以试着一边敲代码,一边体会、思考和学习Kotlin语言中这些最基础的语法知识了。那么下面我们就来看下,在Kotlin语言中是如何定义变量的吧。 ## 变量 在Java/C当中,如果我们要声明变量,我们必须要声明它的类型,后面跟着变量的名称和对应的值,然后以分号结尾。就像这样: ```java Integer price = 100; ``` 而Kotlin则不一样,我们要使用“**val**”或者是“**var**”这样的关键字作为开头,后面跟“变量名称”,接着是“变量类型”和“赋值语句”,最后是分号结尾。就像这样: ```plain /* 关键字 变量类型 ↓ ↓ */ var price: Int = 100; /* ↑ ↑ 变量名 变量值 */ ``` 不过,像Java那样每写一行代码就写一个分号,其实也挺麻烦的。所以为了省事,在Kotlin里面,我们一般会把代码末尾的分号省略,就像这样: ```plain var price: Int = 100 ``` 另外,由于Kotlin支持**类型推导**,大部分情况下,我们的变量类型可以省略不写,就像这样: ```plain var price = 100 // 默认推导类型为: Int ``` 还有一点我们要注意,就是在Kotlin当中,我们应该尽可能避免使用var,**尽可能多地去使用val**。 ```plain var price = 100 price = 101 val i = 0 i = 1 // 编译器报错 ``` 原因其实很简单: * val声明的变量,我们叫做**不可变变量**,它的值在初始化以后就无法再次被修改,它相当于Java里面的final变量。 * var声明的变量,我们叫做**可变变量**,它对应Java里的普通变量。 ## 基础类型 了解了变量类型如何声明之后,我们再来看下Kotlin中的基础类型。 基础类型,包括我们常见的数字类型、布尔类型、字符类型,以及前面这些类型组成的数组。这些类型是我们经常会遇到的概念,因此我们把它统一归为“基础类型”。 ### 一切都是对象 在Java里面,基础类型分为原始类型(Primitive Types)和包装类型(Wrapper Type)。比如,整型会有对应的int和Integer,前者是原始类型,后者是包装类型。 ```java int i = 0; // 原始类型 Integer j = 1; // 包装类型 ``` Java之所以要这样做,是因为原始类型的开销小、性能高,但它不是对象,无法很好地融入到面向对象的系统中。而包装类型的开销大、性能相对较差,但它是对象,可以很好地发挥面向对象的特性。在 [JDK源码](https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/Integer.java)当中,我们可以看到Integer作为包装类型,它是有成员变量以及成员方法的,这就是它作为对象的优势。 然而,在Kotlin语言体系当中,是没有原始类型这个概念的。这也就意味着,**在Kotlin里,一切都是对象。** ![](https://static001.geekbang.org/resource/image/yy/3b/yyd95b04616943878351867c4d1e063b.jpg?wh=2000x1077) 实际上,从某种程度上讲,Java的类型系统并不是完全面向对象的,因为它存在原始类型,而原始类型并不属于对象。而Kotlin则不一样,它从语言设计的层面上就规避了这个问题,类型系统则是完全面向对象的。 我们看一段代码,来更直观地感受Kotlin的独特之处: ```plain val i: Double = 1.toDouble() ``` 可以发现,由于在Kotlin中,整型数字“1”被看作是对象了,所以我们可以调用它的成员方法toDouble(),而这样的代码在Java中是无法实现的。 ### 空安全 既然Kotlin中的一切都是对象,那么对象就有可能为空。也许你会想到写这样的代码: ```plain val i: Double = null // 编译器报错 ``` 可事实上,以上的代码并不能通过Kotlin编译。这是因为Kotlin强制要求开发者**在定义变量的时候,指定这个变量是否可能为null**。对于可能为null的变量,我们需要在声明的时候,在变量类型后面加一个问号“?”: ```plain val i: Double = null // 编译器报错 val j: Double? = null // 编译通过 ``` 并且由于Kotlin对可能为空的变量类型做了强制区分,这就意味着,“可能为空的变量”无法直接赋值给“不可为空的变量”,当然,反向赋值是没有问题的。 ```plain var i: Double = 1.0 var j: Double? = null i = j // 编译器报错 j = i // 编译通过 ``` Kotlin这么设计的原因也很简单,如果我们将“可能为空的变量”直接赋值给了“不可为空的变量”,这会跟它自身的定义产生冲突。而如果我们实在有这样的需求,也不难实现,只要做个判断即可: ```plain var i: Double = 1.0 val j: Double? = null if (j != null) { i = j // 编译通过 } ``` 好,在了解了Kotlin和Java这两种语言的主要区别后,下面就让我们来全面认识下Kotlin的基础类型。 ### 数字类型 首先,在数字类型上,Kotlin和Java几乎是一致的,包括它们对数字“字面量”的定义方式。 ```plain val int = 1 val long = 1234567L val double = 13.14 val float = 13.14F val hexadecimal = 0xAF val binary = 0b01010101 ``` 这里我也来给你具体介绍下: * 整数默认会被推导为“Int”类型; * Long类型,我们则需要使用“L”后缀; * 小数默认会被推导为“Double”,我们不需要使用“D”后缀; * Float类型,我们需要使用“F”后缀; * 使用“0x”,来代表十六进制字面量; * 使用“0b”,来代表二进制字面量。 但是,对于数字类型的转换,Kotlin与Java的转换行为是不一样的。**Java可以隐式转换数字类型,而Kotlin更推崇显式转换。** 举个简单的例子,在Java和C当中,我们经常直接把int类型赋值给long类型,编译器会自动为我们做类型转换,如下所示: ```java int i = 100; long j = i; ``` 这段代码按照Java的编程思维方式来看,的确好像是OK的。但是你要注意,虽然Java编译器不会报错,可它仍然可能会带来问题,因为它们本质上不是一个类型,int、long、float、double这些类型之间的互相转换是存在精度问题的。尤其是当这样的代码掺杂在复杂的逻辑中时,在碰到一些边界条件的情况下,即使出现了Bug也不容易排查出来。 所以,同样的代码,在Kotlin当中是行不通的: ```plain val i = 100 val j: Long = i // 编译器报错 ``` 在Kotlin里,这样的隐式转换被抛弃了。正确的做法应该是显式调用Int类型的toLong()函数: ```plain val i = 100 val j: Long = i.toLong() // 编译通过 ``` 其实,如果我们仔细翻看Kotlin的源代码,会发现更多类似的函数,比如toByte()、toShort()、toInt()、toLong()、toFloat()、toDouble()、toChar()等等。Kotlin这样设计的优势也是显而易见的,**我们代码的可读性更强了,将来也更容易维护了**。 ### 布尔类型 然后我们再来了解下Kotlin中布尔类型的变量,它只有两种值,分别是**true****和****false**。布尔类型支持一些逻辑操作,比如说: * “&”代表“与运算”; * “|”代表“或运算”; * “!”代表“非运算”; * “&&”和“||”分别代表它们对应的“短路逻辑运算”。 ```plain val i = 1 val j = 2 val k = 3 val isTrue: Boolean = i < j && j < k ``` ### 字符:Char Char用于代表单个的字符,比如`'A'`、`'B'`、`'C'`,字符应该用单引号括起来。 ```plain val c: Char = 'A' ``` 如果你有Java或C的使用经验,也许会写出这样的代码: ```plain val c: Char = 'A' val i: Int = c // 编译器报错 ``` 这个问题其实跟前面Java的数字类型隐式转换的问题类似,所以针对这种情况,我们应该调用对应的函数来做类型转换。这一点我们一定要牢记在心。 ```plain val c: Char = 'A' val i: Int = c.toInt() // 编译通过 ``` ### 字符串:String 字符串(String),顾名思义,就是一连串的字符。和Java一样,Kotlin中的字符串也是不可变的。在大部分情况下,我们会使用双引号来表示字符串的字面量,这一点跟Java也是一样的。 ```plain val s = "Hello Kotlin!" ``` 不过与此同时,Kotlin还为我们提供了非常简洁的**字符串模板**: ```plain val name = "Kotlin" print("Hello $name!") /* ↑ 直接在字符串中访问变量 */ // 输出结果: Hello Kotlin! ``` 这样的特性,在Java当中是没有的,这是Kotlin提供的新特性。虽然说这个字符串模板功能,我们用Java也同样可以实现,但它远没有Kotlin这么简洁。在Java当中,我们必须使用两个“+”进行拼接,比如说`("Hello" + name + "!")`。这样一来,在字符串格式更复杂的情况下,代码就会很臃肿。 当然,如果我们需要在字符串当中引用更加复杂的变量,则需要使用花括号将变量括起来: ```plain val array = arrayOf("Java", "Kotlin") print("Hello ${array.get(1)}!") /* ↑ 复杂的变量,使用${} */ // 输出结果: Hello Kotlin! ``` 另外,Kotlin还新增了一个**原始字符串**,是用三个引号来表示的。它可以用于存放复杂的多行文本,并且它定义的时候是什么格式,最终打印也会是对应的格式。所以当我们需要复杂文本的时候,就不需要像Java那样写一堆的加号和换行符了。 ```plain val s = """ 当我们的字符串有复杂的格式时 原始字符串非常的方便 因为它可以做到所见即所得。 """ print(s) ``` ### 数组 最后,我们再来看看Kotlin中数组的一些改变。 在Kotlin当中,我们一般会使用**arrayOf()**来创建数组,括号当中可以用于传递数组元素进行初始化,同时,Kotlin编译器也会根据传入的参数进行类型推导。 ```plain val arrayInt = arrayOf(1, 2, 3) val arrayString = arrayOf("apple", "pear") ``` 比如说,针对这里的arrayInt,由于我们赋值的时候传入了整数,所以它的类型会被推导为整型数组;对于arrayString,它的类型会被推导为字符串数组。 而你应该也知道,在Java当中,数组和其他集合的操作是不一样的。举个例子,如果要获取数组的长度,Java中应该使用“array.length”;但如果是获取List的大小,那么Java中则应该使用“list.size”。这主要是因为数组不属于Java集合。 不过,Kotlin在这个问题的处理上并不一样。**虽然Kotlin的数组仍然不属于集合,但它的一些操作是跟集合统一的。** ```plain val array = arrayOf("apple", "pear") println("Size is ${array.size}") println("First element is ${array[0]}") // 输出结果: Size is 2 First element is apple ``` 就比如说,以上代码中,我们直接使用array.size就能拿到数组的长度。 ## 函数声明 好,了解了Kotlin中变量和基础类型的相关概念之后,我们再来看看它的函数是如何定义的。 在Kotlin当中,函数的声明与Java不太一样,让我们看一段简单的Kotlin代码: ```plain /* 关键字 函数名 参数类型 返回值类型 ↓ ↓ ↓ ↓ */ fun helloFunction(name: String): String { return "Hello $name !" }/* ↑ 花括号内为:函数体 */ ``` 可以看到,在这段代码中: * 使用了**fun关键字**来定义函数; * **函数名称**,使用的是[驼峰命名法](https://zh.wikipedia.org/zh/%E9%A7%9D%E5%B3%B0%E5%BC%8F%E5%A4%A7%E5%B0%8F%E5%AF%AB)(大部分情况下); * **函数参数**,是以(name: String)这样的形式传递的,这代表了参数类型为String类型; * **返回值类型**,紧跟在参数的后面; * 最后是**花括号内的函数体**,它代表了整个函数的逻辑。 另外你可以再注意一个地方,前面代码中的helloFunction函数,它的函数体实际上只有一行代码。那么针对这种情况,我们其实就可以省略函数体的花括号,直接使用“=”来连接,将其变成一种类似变量赋值的函数形式: ```plain fun helloFunction(name: String): String = "Hello $name !" ``` 这种写法,我们称之为**单一表达式函数**。需要注意的是,在这种情况下,表达式当中的“return”是需要去掉的。 另外,由于Kotlin支持类型推导,我们在使用单一表达式形式的时候,返回值的类型也可以省略: ```plain fun helloFunction(name: String) = "Hello $name !" ``` 看到这里,你一定能体会到Kotlin的魅力。它的语法非常得简洁,并且是符合人类的阅读直觉的,我们读这样的代码,就跟读自然语言一样轻松。 然而,Kotlin的优势不仅仅体现在函数声明上,在函数调用的地方,它也有很多独到之处。 ### 函数调用 以我们前面定义的函数为例子,如果我们想要调用它,代码的风格和Java基本一致: ```plain helloFunction("Kotlin") ``` 不过,Kotlin提供了一些新的特性,那就是**命名参数**。简单理解,就是它允许我们在调用函数的时候传入“形参的名字”。 ```plain helloFunction(name = "Kotlin") ``` 让我们看一个更具体的使用场景: ```plain fun createUser( name: String, age: Int, gender: Int, friendCount: Int, feedCount: Int, likeCount: Long, commentCount: Int ) { //.. } ``` 这是一个包含了很多参数的函数,在Kotlin当中,针对参数较多的函数,我们一般会**以纵向的方式排列**,这样的代码更符合我们从上到下的阅读习惯,省去从左往右翻的麻烦。 但是,如果我们像Java那样调用createUser,代码就会非常难以阅读: ```plain createUser("Tom", 30, 1, 78, 2093, 10937, 3285) ``` 这里代码中的第一个参数,我们知道肯定是name,但是到了后面那一堆的数字,就会让人迷惑了。这样的代码不仅难懂,同时还不好维护。 但如果我们这样写呢? ```plain createUser( name = "Tom", age = 30, gender = 1, friendCount = 78, feedCount = 2093, likeCount = 10937, commentCount = 3285 ) ``` 可以看到,在这段代码中,我们把函数的形参加了进来,形参和实参用“=”连接,建立了两者的对应关系。对比前面Java风格的写法,这样的代码可读性更强了。如果将来你想修改likeCount这个参数,也可以轻松做到。这其实就体现出了Kotlin命名参数的**可读性**与**易维护性**两个优势。 而除了命名参数这个特性,Kotlin还支持**参数默认值**,这个特性在参数较多的情况下同样有很大的优势: ```plain fun createUser( name: String, age: Int, gender: Int = 1, friendCount: Int = 0, feedCount: Int = 0, likeCount: Long = 0L, commentCount: Int = 0 ) { //.. } ``` 我们可以看到,gender、friendCount、feedCount、likeCount、commentCount这几个参数都被赋予了默认值。这样做的好处就在于,我们在调用的时候可以省很多事情。比如说,下面这段代码就只需要传3个参数,剩余的4个参数没有传,但是Kotlin编译器会自动帮我们填上默认值。 ```plain createUser( name = "Tom", age = 30, commentCount = 3285 ) ``` 对于无默认值的参数,编译器会强制要求我们在调用处传参;对于有默认值的参数,则可传可不传。Kotlin这样的特性,在一些场景下就可以极大地提升我们的开发效率。 而如果是在Java当中要实现类似的事情,我们就必须手动定义“3个参数的createUser函数”,或者是使用Builder设计模式。 ## 流程控制 在Kotlin当中,流程控制主要有if、when、for、 while,这些语句可以控制代码的执行流程。它们也是体现代码逻辑的关键。下面我们就来一一学习下。 ### if if语句,在程序当中主要是用于逻辑判断。Kotlin当中的if与Java当中的基本一致: ```plain val i = 1 if (i > 0) { print("Big") } else { print("Small") } 输出结果: Big ``` 可以看到,由于i大于0,所以程序会输出“Big”,这很好理解。不过Kotlin的if,并不是程序语句(Statement)那么简单,它还可以作为**表达式**(Expression)来使用。 ```plain val i = 1 val message = if (i > 0) "Big" else "Small" print(message) 输出结果: Big ``` 以上的代码其实跟之前的代码差不多,它们做的是同一件事。不同的是,我们把if当作表达式在用,将if判断的结果,赋值给了一个变量。同时,Kotlin编译会根据if表达式的结果自动推导出变量“message”的类型为“String”。这种方式就使得Kotlin的代码更加简洁。 而类似的逻辑,如果要用Java来实现的话,我们就必须先在if外面定义一个变量message,然后分别在两个分支内对message赋值: ```java int i = 1 String message = "" if (i > 0) { message = "Big" } else { message = "Small" } print(message) ``` 这样两相对比下,我们会发现Java的实现方式明显丑陋一些:**不仅代码行数更多,逻辑也松散了**。 另外,由于Kotlin当中明确规定了类型分为“可空类型”“不可空类型”,因此,我们会经常遇到可空的变量,并且要判断它们是否为空。我们直接来看个例子: ```plain fun getLength(text: String?): Int { return if (text != null) text.length else 0 } ``` 在这个例子当中,我们把if当作表达式,如果text不为空,我们就算出它的长度;如果它为空,长度就取0。 但是,如果你实际使用Kotlin写过代码,你会发现:在Kotlin中,类似这样的判断逻辑出现得非常频繁,如果每次都要写一个完整的if else分支,其实也很麻烦。 为此,Kotlin针对这种情况就提供了一种简写,叫做**Elvis表达式**。 ```plain fun getLength(text: String?): Int { return text?.length ?: 0 } ``` 可以看到,通过Elvis表达式,我们就再也不必写“`if (xxx != null) xxx else xxx`”这样的赋值代码了。它在提高代码可读性的同时,还能提高我们的编码效率。 ### when when语句,在程序当中主要也是用于逻辑判断的。当我们的代码逻辑只有两个分支的时候,我们一般会使用if/else,而在大于两个逻辑分支的情况下,我们使用when。 ```plain val i: Int = 1 when(i) { 1 -> print("一") 2 -> print("二") else -> print("i 不是一也不是二") } 输出结果: 一 ``` when语句有点像Java里的switch case语句,不过Kotlin的when更加强大,它同时也可以**作为表达式,为变量赋值**,如下所示: ```plain val i: Int = 1 val message = when(i) { 1 -> "一" 2 -> "二" else -> "i 不是一也不是二" // 如果去掉这行,会报错 } print(message) ``` 另外,与switch不一样的是,when表达式要求它里面的逻辑分支必须是完整的。举个例子,以上的代码,如果去掉else分支,编译器将报错,原因是:i的值不仅仅只有1和2,这两个分支并没有覆盖所有的情况,所以会报错。 ### 循环迭代:while与for 首先while循环,我们一般是用于重复执行某些代码,它在使用上和Java也没有什么区别: ```plain var i = 0 while (i <= 2) { println(i) i++ } var j = 0 do { println(j) j++ } while (j <= 2) 输出结果: 0 1 2 0 1 2 ``` 但是对于for语句,Kotlin和Java的用法就明显不一样了。 在Java当中,for也会经常被用于循环,经常被用来替代while。不过,**Kotlin的for语句更多的是用于“迭代”。**比如,以下代码就代表了迭代array这个数组里的所有元素,程序会依次打印出:“1、2、3”。 ```plain val array = arrayOf(1, 2, 3) for (i in array) { println(i) } ``` 而除了迭代数组和集合以外,Kotlin还支持迭代一个“区间”。 首先,要定义一个区间,我们可以使用“`..`”来连接数值区间的两端,比如“`1..3`”就代表从1到3的闭区间,左闭右闭: ```plain val oneToThree = 1..3 // 代表 [1, 3] ``` 接着,我们就可以使用for语句,来对这个闭区间范围进行迭代: ```plain for (i in oneToThree) { println(i) } 输出结果: 1 2 3 ``` 甚至,我们还可以**逆序迭代**一个区间,比如: ```plain for (i in 6 downTo 0 step 2) { println(i) } 输出结果: 6 4 2 0 ``` 以上代码的含义就是逆序迭代一个区间,从6到0,每次迭代的步长是2,这意味着6迭代过后,到4、2,最后到0。**需要特别注意的是**,逆序区间我们不能使用“`6..0`”来定义,如果用这样的方式来定义的话,代码将无法正常运行。 好了,那么到目前为止,Kotlin的变量、基础类型、函数、流程控制,我们就都已经介绍完了。掌握好这些知识点,我们就已经可以写出简单的程序了。当然,我们的Kotlin学习之路才刚刚开始,在下节课,我会带你来学习Kotlin面向对象相关的知识点。 ## 小结 学完了这节课,现在我们知道虽然Kotlin和Java的语法很像,但在一些细节之处,Kotlin总会有一些新的东西。如果你仔细琢磨这些不同点,你会发现它正是大部分程序员所需要的。举个例子,作为开发者,我们都讨厌写冗余的代码,喜欢简洁易懂的代码。那么在今天学完了基础语法之后,我们可以来看看Kotlin在这方面都做了哪些改进: * 支持类型推导; * 代码末尾不需要分号; * 字符串模板; * 原始字符串,支持复杂文本格式; * 单一表达式函数,简洁且符合直觉; * 函数参数支持默认值,替代Builder模式的同时,可读性还很强; * if和when可以作为表达式。 同时,JetBrains也非常清楚开发者在什么情况下容易出错,所以,它在语言层面也做了很多改进: * 强制区分“可为空变量类型”和“不可为空变量类型”,规避空指针异常; * 推崇不可变性(val),对于没有修改需求的变量,IDE会智能提示开发者将“var”改为“val”; * 基础类型不支持隐式类型转换,这能避免很多隐藏的问题; * 数组访问行为与集合统一,不会出现array.length、list.size这种恼人的情况; * 函数调用支持命名参数,提高可读性,在后续维护代码的时候不易出错; * when表达式,强制要求逻辑分支完整,让你写出来的逻辑永远不会有漏洞。 ![图片](https://static001.geekbang.org/resource/image/32/67/32ab3d37cd7f9650f4cba17736305c67.jpg?wh=1920x1983) 这些都是Kotlin的**闪光点**,也是它最珍贵的地方。 这一切,都得益于Kotlin的发明者JetBrains。作为最负盛名的IDE创造者,JetBrains能深刻捕捉到开发者的需求。它知道开发者喜欢什么、讨厌什么,它甚至知道开发者容易犯什么样的错误,从而在语言设计的层面规避错误。站在这个角度看,JetBrains能够创造出炙手可热的Kotlin语言,就一点都不奇怪了。 以上这么多的“闪光点”还仅仅只是局限于我们这节课的内容,如果放眼全局,这样的例子更是数不胜数。**Kotlin对比Java的提升,如果独立去看其中的某一个点,都不足以让一个开发者心动。不过,一旦这样的改善积少成多,Kotlin的优势就会显得尤为明显。**这也是很多程序员表示“Kotlin用过了就回不去”的原因。 ## 思考题 虽然Kotlin在语法层面摒弃了“原始类型”,但有时候为了性能考虑,我们确实需要用“原始类型”。这时候我们应该怎么办? 欢迎在评论区分享你的思路,这个问题我会在第三节课给出答案,我们下节课再见。