Kotlin核心编程 第一章认识Kotlin、第二章基础语法

本文是Kotlin核心编程(2021年6月第一版第5次印刷)的读书笔记。
感觉适合有一定了解java的Kotlin初学者,内容讲了Kotlin通用的使用场景、方法,原理性内容不是很难。
这里会根据书中顺序,把个人感觉比较重要的内容做下记录。

第一章、认识Kotlin

1.3Kotlin---改良的Java

1在很大程度上实现了类型推导,而java在se10才支持了局部变量的推导;
2放弃了static关键字,但又引入了object,可以直接用它来声明一个单例。
3引入了java中没有的特殊类,比如Data Class(数据类)、Sealed Classes(密封类)。
4新增了java中没有的语法糖(Smart Casts)。

//java写法:
if(parentView instanceOf  ViewGroup){
        ((ViewGroup)parentView).addView(childView)
}
//kotlin写法:
if(parentView is ViewGroup){
        parentView.addView(childView)
}

兼容了java,Kotlin可以与java 6一起工作,这也是在Android 上流行的原因之一。

第二章、基础语法

2.1不一样的类型声明:

类型名放变量名后面

//String a = "I am Kotlin";
val a :String = "I am Kotlin"

增强的类型推导:编译器可以在不显示声明类型的情况下,自动推导出他所需要的类型。

val string = "I am Kotlin"  //java.lang.String
val int = 11234  //int
val long = 1234L  //long
...

声明函数返回值类型,类型信息放在函数名后面

fun sum(x:Int,y:Int):Int{return x+y}

如果没有声明返回值类型,函数默认被当做返回Unit类型,然而实际上返回的是Int,所以编译器会报错。这种情况下必须显示声明返回值类型。

fun sum(x:Int,y:Int){return x+y}//!
Type mismatch: inferred type is Int but Unit was expected

*可以暂时把Unit当做java中的void。Unit是一个类型,void只是一个关键字。

kotlin进一步增强了函数的语法,可以把{}去掉,用等号定义一个函数。

fun sum(x:Int,y:Int)=x+y

这种用单行表达式与等号的语法来定义函数,叫做表达式函数体,作为区分,普通的函数声明则可以叫做代码块函数体。使用表达式函数体我们可以不声明返回值类型。但是kotlin并不能针对递归情况进行全局类型推导

fun foo(n:Int) = if(n == 0) 1 else n*foo(n-1)//!

Type checking has run into a recursive problem.
 Easiest workaround: specify types of your declarations explicitly

2.2val和var的使用规则

var代表了变量,val声明的变量具有java中final关键字的效果,也就是引用不可变。

val x = intArrayOf(1,2,3)
x = intArrayOf(2,3,4)//!
Val cannot be reassigned
//因为引用不可变,
//所以x不能指向另一个数组,
//但我们可以修改x指向数组的值
x[0] = 2
println(x[0])
2

更加直观的例子

class Book (var name:String){//var生命的参数name引用可以被改变
    fun printlnName(){
        println(this.name)
    }
}
fun main() {
    val book = Book("Think in java")//book对象的引用不可变
    book.name = "kotlin"
    book.printlnName()  //"kotlin"
}

2.3高阶函数和Lambda

我们可以不经像类一样在顶层直接定义一个函数,也可以在一个函数内部定义一个局部函数,还可以将函数像普通变量一样传递给另一个函数,或在其他函数内部被返回。
高阶函数:接收一个或多个过程作为参数;或者把一个过程作为返回结果。

接下来用一个例子来说明:有一个国家数据库,设计了一个CountryApp对国家数据进行操作,现在要获取所有的欧洲国家

data class Country(
    val name:String,
    val continient:String,
    val population:Int)

class CountryApp{
    fun filterCountry(countries:List<Country>):List<Country>{
        val res = mutableListOf<Country>()
        for(c in countries){
            if(c.continient == "EU"){
                res.add(c)
            }
        }
        return res
    }
}

后来要找其他洲,改进了上述方法,加了一个参数

    fun filterCountry(countries:List<Country>,continient:String):List<Country>{
        val res = mutableListOf<Country>()
        for(c in countries){
            if(c.continient == continient){
                res.add(c)
            }
        }
        return res
    }

现在要增加人口的条件,又增加了一个参数,如下:

fun filterCountry(countries:List<Country>,continient:String,population:Int):List<Country>{
        val res = mutableListOf<Country>()
        for(c in countries){
            if(c.continient == continient&& c.population>population){
                res.add(c)
            }
        }
        return res
    }

如果更多的筛选条件会作为方法参数不断累加,业务逻辑也高度耦合。需要把所有的筛选逻辑行为都抽象成一个参数---把筛选逻辑作为一个方法传入。

class CountryTest{
    fun isBigCountry(country :Country):Boolean{
        return country.continient == "EU" && country.population>10000
    }
}

Kotlin中函数类型格式非常简单,有以下几个特点:
通过->来组织参数类型和返回值类型;
用一个括号包裹参数类型;
返回值即使是Unit也必须显示声明。
*如果是无参函数类型,参数部分用()代替;多个参数用逗号分隔

(Int)->Unit
()->Unit
(Int,String)->Unit
(errCode:Int,errMsg:String)->Unit
(errCode:Int,errMsg:String?)->Unit
((errCode:Int,errMsg:String?)->Unit)?
(Int)->((Int)->Unit)

最终上面的方法修改为:

fun filterCountry(
        countries:List<Country>,
        test:(Country)->Boolean
    ):List<Country>{
        val res = mutableListOf<Country>()
        for(c in countries){
            if(test(c)){
                res.add(c)
            }
        }
        return res
    }

然后我们需要把isBigCountry方法传递给filterCountry,需要一个方法引用表达式通过::实现对于某个类的方法进行引用。

fun main() {
    val countryApp = CountryApp()
    val countryTest = CountryTest()
    val countries = ......
    countryApp.filterCountry(countries,countryTest::isBigCountry)
}

上面仍不算一个好的方案,每次都要在类中专门写一个新增的筛选方法。用匿名函数进行进一步优化。

countryApp.filterCountry(
    countries,
    fun(country:Country):Boolean{
        return country.continient == "EU"
        && country.population >10000
    }
)

还有另一种Lambda表达式让代码更简洁,可以理解为简化表达式后的匿名函数

countryApp.filterCountry(
        countries,
        {
            country->
            country.continient == "EU" && country.population >10000
        }
)

Lambda语法:
一个lambda表达式必须通过{}来包裹;
如果lambda声明了参数部分类型,且返回值类型支持类型推导,那么lambda变量就可以省略函数类型声明;
如果lambda变量声明了函数类型,那么lambda的参数部分类型就可以省略。
此外,如果lambda表达式返回的不是Unit,则默认最后一行表达式的值类型就是返回值类型。

lambda里的it是kotlin简化后的一种语法糖,叫做单个参数的隐式名称。代表这个lambda接收的单个参数。

扩展函数--kotlin允许我们在不修改已有类的前提下,给他新增方法:

fun View.invisible(){
    this.visibility = View.INVISIBLE
}

类型View被称为接收者类型,this对应的是这个类型所创建的接收者对象,可以被省略。

2.4面向表达式编程

*kotlin里的try catch finally(经过测试和java一致)

在下述4种特殊情况时,finally块都不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。

fun main() {
    println(test())
}

fun test ():Int{
    try{
        println("try ----")
        var i = 1/0
        return 0;
    }catch(e:Exception){
        println("catch ----")
        return 1;
    }finally{
        println("finally ----")
        return 2;
    }
}

//输出:
try ----
catch ----
finally ----
2
fun main() {
    println(test())
}

fun test ():Int{
    try{
        println("try ----")
        var i = 1/0
        return 0;
    }catch(e:Exception){
        println("catch ----")
        return 1;
    }finally{
        println("finally ----")
        //return 2; 没有return后
    }
}

//输出:
try ----
catch ----
finally ----
1

假如我们不在 finally中 return,结果会怎样

fun main() {
    println(test())
}
fun test ():Int{
    var i = 999;
    try{
        println("try ----")
        var i = 1/0
        return 0;
    }catch(e:Exception){
        println("catch ----")
        i = 100;
        return i;
    }finally{
        println("finally ----")
        i = 200;
        return i;
    }
}

//输出:
try ----
catch ----
finally ----
200
fun main() {
    println(test())
}
fun test ():Int{
    var i = 999;
    try{
        println("try ----")
        var i = 1/0
        return 0;
    }catch(e:Exception){
        println("catch ----")
        i = 100;
        return i;
    }finally{
        println("finally ----")
        i = 200;
        //return i;没有return后
    }
}

//输出:
try ----
catch ----
finally ----
100

在 return的时候会把返回值压入栈,并把返回值赋值给栈中的局部变量, 最后把栈顶的变量值作为函数返回值。所以在 finally中的返回值就会覆盖 try/catch中的返回值,如果 finally中不执行 return语句,在 finally中修改返回变量的值,不会影响返回结果。

Kotlin中的“?:”被叫做Elvis运算符,表示一种类型的可空性

Kotlin中的when表达式:
由when开始,{}包含多个分支,每个分支用->连接,不需要switch 和 break,由上到下,依次匹配否则执行else;
最终整个when表达式的返回类型就是所有分支相同的返回类型,或者公共类型。

fun foo(a:Int) = when(a){
    1->1
    2->2
    else ->0
}
或者:
fun foo(a:Int) = when{
    a==1->1
    a==2->2
    else ->0
}

kotlin中的for循环

for (i in 1..10)println(i)
或者
for (i:Int  in 1..10)println(i)

范围表达式range通过rangeTo函数实现的,通过..操作符与某种类型对象组成。除了整形的基本类型之外,该类型需要实现java.lang.Comparable接口
字符串的大小根据首字母在字母表中的排序比较,相同则从左到右一次比较
step函数来定义迭代步长

for (i in 1..10 step 2)println(i)

downTo实现倒序

for (i in 1..10 downTo 1 step 2)println(i)//通过downTo 而不是10..1
//108642

util实现半开区间

for (i in 1 util10)println(i)
//123456789

用in来检查成员关系

"a" in listOf("a","b","c")

通过withIndex提供一个键值元组

for((index,value) in array.withIndex()){
    println("the element at $index is $value")
}

in、step、downTo、until是通过中缀表达式实现的

2.5字符串的定义和操作

val str = "hello world!"

str.length//12
str.substring(0,5)//hello
str+"hello kotlin!"//hello world!hello kotlin!
str[0]//h
str.first()//h

"".isEmpty()//t
"".isBlank()//t
用三个引号定义的字符创,最终的打印格式和在代码里的格式一致,而且不会解释转化转义字符

    val html = """<html>
                        <body>
                            <p>hello</p>
                        </body>
                   </html>  
                """

字符串模板${}提升紧凑型和可读性

Hi ${name},welcome  to ${lang}

字符串判等
==判断内容是否相等
===判断引用是否相等

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容