这一节名为 “i_introduction(介绍)”,主要向我们展示了Kotlin的语法,特性,与JAVA的相似与不同。
在使用Kotlin-koans学习时,我们的练习都在src
目录下,打开后可以看到我们的练习,我们要做的就是根据提示完成练习,并运行test
目录下对应的单元测试,如果单元测试报错,说明没有通过,若什么都没输出,则说明完成了练习。
0. _0_Hello_World
先贴一下代码
package i_introduction._0_Hello_World
import util.TODO
import util.doc0
fun todoTask0(): Nothing = TODO(
"""
Task 0.
Read README.md to learn how to work with this project and check your solutions.
Using 'documentation =' below the task description you can open the related part of the online documentation.
Press 'Ctrl+Q'(Windows) or 'F1'(Mac OS) on 'doc0()' to call the "Quick Documentation" action;
"See also" section gives you a link.
You can see the shortcut for the "Quick Documentation" action used in your IntelliJ IDEA
by choosing "Help -> Find Action..." (in the top menu), and typing the action name ("Quick Documentation").
The shortcut in use will be written next to the action name.
Using 'references =' you can navigate to the code mentioned in the task description.
Let's start! Make the function 'task0' return "OK". Note that you can return expression directly.
""",
documentation = doc0(),
references = { task0(); "OK" }
)
fun task0(): String {
return todoTask0()
}复制代码
阅读TODO中的内容,首先要求我们阅读README.md
文件,学习如何使用该项目,了解通过该项目能学到什么,并给出了Hello_World练习的答案。然后是教我们使用在线文档,可以看到代码中有一个doc0()
函数,将光标移动到该函数位置,按下 'Ctrl + Q'(Mac OS按 'F1')可以打开快速文档,这里是Kotlin
的详细文档,点击链接便可在浏览器中查看。注意一定要将光标移动到该函数位置,只是将鼠标悬停在函数上方是不行的。
接下来就是本练习的内容了,修改task0()
函数使其返回一个"OK"
fun task0(): String {
return todoTask0()
}复制代码
练习中所有todo
前缀的函数都是为了说明练习内容和不让编辑器报错的,我们修改时不需要改动它们,不要调用就可以了。
开始练习
fun task0(): String {
return "OK"
}复制代码
完成,也可以这样,省略函数体,直接给函数赋值
fun task0(): String = "OK"复制代码
还可以这样,连返回值类型都省略
fun task0() = "OK"复制代码
然后打开test
目录,找到对应的单元测试,运行
不出意外我们将看到如下结果
这就说明我们用
Kotlin
说出了
Hello World
,马不停蹄地开始下一个项目吧。
1. _1_Java_To_Kotlin_Converter
该练习教我们使用把Java
代码转为Kotlin
代码的工具,Intellj IDEA 自带了这个工具,我们将JavaCode1.java
中的task1
方法复制到n01JavaToKotlinConverter.kt
中,直接覆盖对应的task1
函数就好了
- 选择 => 复制
- 粘贴 => Yes
- 转换就完成了
运行测试,通过
2. _2_Named_Arguments
在Kotlin
中,我们在调用方法或函数的时候,可以使用方法或函数中形参的名字传参,也可以为形参设置默认值
如
fun bar(i: Int, s: String = "", b: Boolean = true) {}复制代码
可以看到函数bar()
中有三个形参i,s,b
,s
和b
都设置了默认值,所以调用的时候必须传递的参数就只有i
了
bar(1)复制代码
因为可以使用形参的名字传参,我们在调用函数的时候就不用关心参数的顺序了
bar(1, b = fasle)复制代码
好方便
练习要求我们使用库函数joinToString
设置前后缀{
和}
,我们使用形参名传值
fun task2(collection: Collection<Int>): String {
return collection.joinToString(postfix = "}",prefix = "{")
}复制代码
完成,参数顺序打乱都没问题
3. _3_Default_Arguments
使用参数默认值,修改foo
函数,实现Java
中需要重载才能实现的功能
fun foo(name: String, number: Number = 42, toUpperCase: Boolean = false): String {
return (if (toUpperCase) name.toUpperCase() else name) + number
}
fun task3(): String {
return (foo("a") +
foo("b", number = 1) +
foo("c", toUpperCase = true) +
foo(name = "d", number = 2, toUpperCase = true))
}复制代码
完成,太方便了。
这里我们注意到,Kotlin
中的if语句可以直接返回结果,不需要写return
语句,而我们熟悉的三目运算符 ?:
在Kotlin
中另有它用。
4. _4_Lambdas
使用lambda表达式,遍历集合,不能使用Iterables
,包含偶数返回ture
,反之false
fun task4(collection: Collection<Int>): Boolean = collection.any { x -> x % 2 == 0 }复制代码
调用Collection
的any
方法,并完成表达式即可,any
表示集合中有任一元素符合表达式,结果即为true
,我关于Lambda表达式的内容了解很少,还要专门学习。
5. _5_String_Templates
字符串模板"Any expression can be used: ${if (c) x else y}"
,使用${}
可在字符串中使用变量或表达式,用"""String"""
,字符串中不需要用\
来转义,可以直接使用各种符号,包括换行符
修改模板字符串,使其可以匹配测试中的日期格式
fun task5(): String ="""\d{2}\ ${month}\ \d{4}"""复制代码
6. _6_Data_Classes
数据类,Kotlin
中专门用来构造数据实体类的方式,非常简洁
补全Person
data class Person(val name: String, val age: Int)
7. _7_Nullable_Types
可空类型,练习要求我们重构JavaCode7.java
中的sendMessageToClient
方法,但是只能使用一次if 语句,用Kotlin
当然是可以做到的
fun sendMessageToClient(client: Client?, message: String?, mailer: Mailer) {
if (client == null || message == null) return
val personalInfo = client.personalInfo ?: return
val email = personalInfo.email ?: return
mailer.sendMessage(email, message)
}复制代码
函数签名中的跟在类型后边的?表示可传入空值,而在函数体中我们看到了已经不是原来的?:
的?:
,这里的?:
称为elvis
操作符,它的功能是判断变量或表达式的值是否为空,非空返回操作符左边,空则返回右边,?:
是一个整体,不可在中间加入其他东西。
8. _8_Smart_Casts
使用智能转换和when
表达式完成eval方法,实现和JavaCode8.java
中同样的功能
fun eval(e: Expr): Int =
when (e) {
is Num -> e.value
is Sum -> eval(e.left) + eval(e.right)
else -> throw IllegalArgumentException("Unknown expression")
}复制代码
在Kotlin
中没有switch
表达式,但是when
提供了更丰富的功能,在when
中,我们使用->
表示执行某一分支后的操作,->
之前是条件,条件不一定是某个值,它可以是任意表达式。
多个条件执行同一操作,可用,
分隔
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}复制代码
可以用in
或!in
选择在或不在某一范围
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}复制代码
可以用is
智能转换类型,如同本练习
当when
没有参数的时候,也可以用来替代if-else if
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}复制代码
还有,当编译器认为我们提供的条件分支没有覆盖所以情况时,我们必须使用else
,它相当于switch
中的default
9. _9_Extension_Functions
Kotlin
支持为已经存在的类编写扩展方法,而且不需要对原有类进行任何改动,只需要使用className.extensionFun
的形式就可以了
fun String.lastChar() = this.get(this.length - 1)复制代码
本练习要求我们使用扩展方法为Int
和Pair
扩展r
方法,r
方法的功能是构造一个RationalNumber
类的对象
fun Int.r(): RationalNumber = RationalNumber(this,1)
fun Pair<Int, Int>.r(): RationalNumber = RationalNumber(first,second)复制代码
简单灵活,非常强大
10. _10_Object_Expressions
在JAVA
中我们要实现一个接口或使用一个抽象类时通常会使用匿名内部类,在Kotlin
中,我们使用Object Expressions
【对象表达式】,看上去这和在Java
中没有太大不同
本练习中我们使用对象表达式来实现Collections.sort
中的Comparator
fun task10(): List<Int> {
val arrayList = arrayListOf(1, 5, 2)
Collections.sort(arrayList, object : Comparator<Int> {
override fun compare(o1: Int, o2: Int): Int = o2 - o1
})
return arrayList
}复制代码
11. _11_SAM_Conversions
Kotlin支持SAM转换,我没弄懂这是什么原理,总之很神奇。使用这个功能,我们可以进一步简化上一练习中的方法
fun task11(): List<Int> {
val arrayList = arrayListOf(1, 5, 2)
Collections.sort(arrayList, { x, y -> y-x })
return arrayList
}复制代码
12. _12_Extensions_On_Collections
Kotlin
的标准库提供为Collections
提供了非常多有用的扩展方法,如上一练习当中的从大到小排序,方法名为sortedDescending
,调用此方法就可方便地完成排序了
fun task12(): List<Int> {
return arrayListOf(1, 5, 2).sortedDescending()
}复制代码