kotlin学习之Kotlin Koans练习

学习资源

Kotlin Koans:Kotlin 在线练习题,适合有一定基础的Kotlin学习者。
简单介绍下该如何去做练习,链接中左边为练习的目录,中间为代码编辑器,右边为这道练习题的介绍。建议在做之前去点一下介绍中高亮的链接,有助于理解。同时博主是一边看《Kotlin in Action》一边练习的,所以这边给出每章读完适宜去做哪几个练习,在给予练习答案的同时,一并讲解所涉及的知识点。
在这里插入图片描述

2 kotlin基础

学完第二章后,大概掌握了函数和变量,类和属性,字符串,when表达式,职能转换,for循环,迭代区间,in等知识,适宜去做接下来几个练习。

Hello, world!

本题的目的是让我们写一个start函数,并返回字符串"OK"。
在这里插入图片描述
我们点开链接,有如下四行代码:
1.一个具有两个Int输入参数和Int返回参数的函数。

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

2.方法可以以表达式形式,由编译器推断出应该返回的类型。

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

3.方法也可以没有返回值(使用Unit)

fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}
//结果
sum of -1 and 8 is 7

4.Unit也可以忽略

fun printSum(a: Int, b: Int) {
    println("sum of $a and $b is ${a + b}")
}

看完链接就知道该怎么在todo处填写代码了

fun start(): String = "OK"

成功后会显示:Passed:testOK。实在想不出来也可以点击Revert按钮右边的show answer按钮。

Data classes

在这里插入图片描述
简单来说,就是让我们写一个类,以实现右边代码中的功能。
我们使用数据类,相比于java中要写get和set函数,而在kotlin中不需要写这些函数。属性可以是只读属性(val)和可写属性(var),只读属性有一个getter,可写属性既有getter和setter。

data class Person(val name: String,var age: Int)

当我们调用Person实例的age属性,相当于是java中调用get函数,而我们更改其值时又相当于调用set函数。

val person = Person("Bob",15)
println(person.age)
person.age = 20
println(person.age)

我们也可以自定义访问器。

data class NewPerson(val name: String,val age: Int)
{
    val isOld:Boolean
    	get(){
        return age>50
    }
}
fun main(args:Array<String>)
{
    val person1 = NewPerson("liu",51)
    val person2 = NewPerson("zhang",20)
    println(person1.isOld)
    println(person2.isOld)
}

接下来我们看看链接说了什么。
编译器除了get和set还帮我们实现了以下方法。(toString能显示出如下前提是data class)
在这里插入图片描述
同时,注意,编译器只使用在主构造函数中为自动生成的函数定义的属性。要从生成的实现中排除属性,请在类主体中声明它。继续看刚刚的例子。我们在Newperson类中又定义了一个新的属性sex。

data class NewPerson(val name: String,val age: Int)
{
    var sex: Int = 0
    val isOld:Boolean
    	get(){
        return age>50
    }
}
fun main(args:Array<String>)
{
    val person1 = NewPerson("liu",51)
    val person2 = NewPerson("liu",51)
     println(person1.equals(person2))
    person2.sex = 1
    println(person1.equals(person2))
    println(person2)
}

我们在main函数中定义了两个变量,他们的name属性与age属性相同,判断二者是否相同,之后将person2的sex属性设置为1,继续判断是否相同,最后输出person2,我们看结果。可以看到只有在类主体中声明的属性才会被用来使用(toString(), equals(), hashCode(), copy())。

true
true
NewPerson(name=liu, age=51)

回到练习,答案现在很清晰了。

data class Person(val name: String,val age: Int)

Smart casts

在这里插入图片描述
这个练习和2.3.5中的小例子是一模一样的,所以这里就整理下涉及到的知识点。
1.智能转换
首先,在kotlin中,要是用is检查来判断一个变量是否是某种类型,和java中的instanceOf相似,不过java中判断之后,还需要进行类型转换,比如当前对象expr是Num类型,在java中还要将expr转换成Num,才可以调用Num的方法或属性。在kotlin中,如果你检查过一个变量是某种类型,后面就不需要再转换它,可以把它当做你检查过的类型使用,这种转换方式就叫智能转换
2.when的用法
when是一个有返回值的表达式,因此可以写一个直接返回when表达式的表达式体函数。

enum class Color{
    Red, Orange, Yellow, Green, Blue, Indigo, Violet
}
fun getColor(color: Color)=when(color){
        Color.Red -> "Richard"
        Color.Orange -> "Of"
        Color.Yellow -> "York"
        Color.Green -> "Gave"
        Color.Blue -> "Battle"
        Color.Indigo -> "In"
        Color.Violet -> "Vain"
    }

和java不同,不需要在每个分支都写上break语句。也可以把多个值合并到同一个分支,只需要用逗号隔开这些值。

Color.Red, Color.red -> "Richard"

when结构比switch更强大的点在于,switch要求必须使用常量(枚举常量,字符串或者数字字面值)作为分支条件,而when允许使用任何对象。setOf函数可以创建出一个Set,它会包含所有指定为函数实参的对象when表达式把它的实参依次和所有分支匹配,直到某个分支满足条件。这里setOf(c1,c2)被用来检查是否和分支条件相等:先和setOf(Red,Yellow)比较,然后是其他颜色的set,一个接一个。如果没有其他的分支满足条件,else分支会执行。

//when中的子分支条件可以是任何对象。而switch中必须是常量
fun  mix(c1: Color,c2: Color) = 
	when(setOf(c1,c2)){
    	setOf(Color.Red,Color.Yellow) -> Color.Orange
    	setOf(Color.Yellow,Color.Blue) -> Color.Green
        setOf(Color.Blue,Color.Violet) -> Color.Indigo
        else -> throw Exception("Dirty color")
}
fun main(arg: Array<String>){
    //println(getColor(Color.Orange))
    println(mix(Color.Yellow,Color.Red))
    println(mix(Color.Yellow,Color.Yellow))
}
//输出
Orange
Exception in thread "main" java.lang.Exception: Dirty color
    at Test2_3Kt.mix(test2_3.kt:20)
    at Test2_3Kt.main(test2_3.kt:25)

when表达式并不仅限于检查值是否相等,它也允许检查when实参值的类型。这也是我们的答案。

fun eval(expr: Expr): Int =
        when (expr) {
            is Num -> TODO()
            is Sum -> TODO()
            else -> throw IllegalArgumentException("Unknown expression")
        }

3.函数的定义与调用

Default arguments

在这里插入图片描述
我们首先看右侧java代码应对不同的输入参数,就要写多个函数。但我们能发现,假如我们只有名字的话
会依次调用这些函数

//1
public String foo(String name) {
    return foo(name, 42);
}
//2
public String foo(String name, int number) {
    return foo(name, number, false);
}
//3
public String foo(String name, int number, boolean toUpperCase) {
    return (toUpperCase ? name.toUpperCase() : name) + number;
}

也就是说number默认为42,toUpperCase默认为false。
接下来我们看下在kotlin中如何为形参赋初始值,点击链接

1.函数参数可以有默认值,当相应的参数被省略时使用默认值。与其他语言相比,这减少了重载的数量。
默认值在类型后使用等号赋值

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) { /*...*/ }

2.如果一个有默认值的参数后跟着一个没有默认值的参数,在使用该函数时就要使用指定实参对应哪个形参( named arguments)
如下代码是错误的,Error:(7, 10) Kotlin: No value passed for parameter ‘baz’

fun foo(bar: Int = 0,baz:Int){
    println("bar is $bar,baz is $baz")
}
fun main(args: Array<String>){
    foo(0)
}

正确代码应该是:

foo(baz = 10)

看完链接后应该知道todo该写什么了。(number默认为42,toUpperCase默认为false)

fun foo(name: String, number: Int=42, toUpperCase: Boolean=false) =
        (if (toUpperCase) name.toUpperCase() else name) + number

fun useFoo() = listOf(
        foo("a"),
        foo("b", number = 1),
        foo("c", toUpperCase = true),
        foo(name = "d", number = 2, toUpperCase = true)
)
发布了55 篇原创文章 · 获赞 28 · 访问量 9241

猜你喜欢

转载自blog.csdn.net/weixin_41796401/article/details/103211574