Kotlin核心编程 - 基础语法
类型声明
-
如果一个函数没有声明返回值的类型,函数会默认被当成返回Unit类型
fun sum(x: Int, y: Int, z: Int) { return x + y + z}
报错,因为默认是Unit ,却return Int类型 -
Kotlin支持这种单行表达式与等号的语法来定义函数,叫做表达式函数体 。普通的函数声明则可叫做代码块函数体
判断是否需要显示声明类型
- 如果它是一个函数的参数。必须使用
- 如果是一个非表达式定义的函数。除了返回Unit,其他情况必须使用
- 如果是一个递归函数。必须使用
- 如果它是一个共有方法的返回值。为了更好的代码可读性及输出类型的可控性,建议使用
val 和 var
-
var 普通变量
-
val 只读变量。相当于java中给变量添加了final 关键字
val虽然声明是只读变量,引用不可更改,但是并不代表其引用对象的可变成员不能修改
class Book(var name: String) {
fun printName() {
println(this.name)
}
}
fun main() {
val book =Book("think in java")
book.name="Diving into kotlin"
book.printName() // Diving into kotlin
虽然声明了一个val 类型的变量 book ,book的引用没变,但是里面的属性name的值可以改变的。
尽可能的采用val,不可变对象及纯函数来设计程序。
纯函数:没有副作用的函数,具备引用透明性。
函数的副作用
副作用:就是修改了某处的某些东西,例如
- 修改的外部变量的值
- IO操作,如数据写入磁盘
- UI操作,如修改了一个按钮的可操作状态
副作用的产生往往和可变数据和共享状态有关。有时候它会使得结果变得难以预测。
在kotlin中,推荐优先使用val 来声明一个本身不可变的变量。可以避免避免副作用。
- 这是一种防御行的编码思维模式,更加安全可靠,因为变量的值永远不会在其他地方被修改(一些框架采用反射技术的情况除外)
- 不可变的变量意味这更加容易推理,越是复杂的业务逻辑,它的优势越大。
高阶函数
函数式语言一个典型的特性: 函数是头等公民。
我们不仅可以像类一样在顶层定义一个函数,也可以在一个函数内部定义一个函数
还可以将一个函数向普通变量一样传递给另外一个函数,或者在其他函数内被返回。
(Int)-> ((Int)-> Unit) //表示传入一个类型为Int的参数,然后返回另一个类型为(Int)-> Unit的函数。
//或者这种
(Int)-> (Int)-> Unit //传入一个(Int)-> Int的函数类型的参数,返回一个Unit
函数: 就是一种定义过程的能力。
过程:总结一些公关的行为,如对数字的加减法,求立方等,被称为过程。 它接收的数字是一种数据,然后也能产生另外一种数据。
高阶函数:以其他函数作为参数或返回值的函数
函数的类型
遵循以下几点
- 通过-> 符号来组织参数类型和返回值类型,左边是参数类型,右边是返回值类型
- 必现通过一个括号包裹参数类型
如果是无参函数,括号内容为空。()->Unit
如果是多个参数,使用逗号隔开。(Int,String)->Unit
- 返回值即时是Unit,也必须显示声明
方法和成员引用
使用 ::
对某个类的方法进行引用,
匿名函数
Kotlin支持在缺省函数名的情况下,直接定义一个函数,
只能存在某个函数体内,或者赋值给某个变量中。不能单独存在。
//赋值给变量
val foo=fun(content: String): Boolean {
return content.equals("en")
}
//存在main()函数内部
fun main() {
fun(content: String): Boolean {
return content.equals("en")
}
}
lambda
和匿名函数一样,是一种函数字面量。具体语法如下:
- 一个lambda表达式必须通过{}来包裹
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y } - 如果lambda声明了参数部分的类型,且返回类型支持类型推导,那么lambda变量就可以省略函数类型声明。
val sum = { x: Int, y: Int -> x + y } ,x 和y 声明了Int类型,那么返回值 x+y就可以省略Int类型声明了 - 如果lambda变量声明的函数类型,那么lambda的参数部分的类型就可以省略
val sum: (Int, Int) -> Int = { x, y -> x + y } ,声明了函数类型是 (Int, Int) -> Int,那么参数部分就可以省略Int
- fun 在没有等号,只有{}的情况下,是我们最常见的代码块函数体,如果返回非Unit,必须带有return
fun foo(x:Int){print(x)}
fun foo(x:Int){ return x*x}
- fun 带有等号,是单表达式函数体,该情况下可以省略return
fun foo(x:Int)= x*x
如果左侧是fun,那么lambda表达式函数体,也必须通过()或者invoke来调用lambda
val foo = { x: Int -> print(x) } //foo.invoke(1)或者foo(1)
fun foo(x: Int) = { y: Int -> x * y }//foo(1).invoke(2)或者foo(1,2)
不带参数的情况
val foo = { print("无参 val ") } //foo() 或者foo.invoke()
fun foo1() = { println("无参 fun") } // foo1().invoke() 或者foo1()()
注意,如果带有参数,{}中必须使用-> 把参数和结果隔开。
fun foo(x: Int) = { y: Int -> x * y }//foo(1).invoke(2)或者foo(1,2)
//等价于
fun foo(x: Int): (Int)->Int{
return { y: Int -> x * y }
} //foo(1)(2)
fun sum(x: Int, y: Int, z: Int) = x + y + z //sum(1, 3, 4)
//等价于
fun sum(x: Int) = { y: Int ->
{ z: Int -> x + y + z }
} //sum(1)(2)(3)
可以看到,第二种方式有点像是击鼓传花,整个过程如下
- 开始的暗号就是第一个参数
- 下个环节的演绎就是返回的函数
- 谜底就是科里化最终执行获得的结果
如果一个函数最后一个参数是函数类型,那么在调用该函数的时候,最后的函数类型可以写道括号外面, 如下所示。
fun curryingLike(content: String, block: (String) -> Unit) {
block(content)
}
//下面四种调用方式是等价的。
curryingLike("look like curring style", { content ->
println(content)
})
curryingLike("look like curring style", {
println(it)
})
curryingLike("look like curring style") { content ->
println(content)
}
curryingLike("look like curring style") {
println(it)
}
如果只有一个参数并且是函数类型,那么括号也可以省略
fun omitParaentheses(block: () -> Unit) {
block()
}
//下面两种方式等价的
omitParaentheses(){
println("omitParaentheses")
}
omitParaentheses {
println("omitParaentheses")
}
闭包
在Kotlin中,匿名函数体,lambda表达式,局部函数,object表达式,在语法上都存在{},由{}包裹的代码块如果访问了外部变量则成为一个闭包。
一个闭包可以被当做参数传递或者直接使用。简单的看成 访问外部环境变量的函数
面向表达式编程
kotlin 的流程控制不再是清一色的普通语句,而是可以返回值的。是一些崭新的表达式语句。例如if-else 表达式,when 表达式,函数体表达式,lambda 表达式等
- 程序的赋值,循环,打印等操作,都可以被称为语句
- 表达式可以是一个值,常量,变量,操作符,函数,或者他们之间的组合,编程语言对其进行解释和计算,以求产生另一个值。简单来说: 表达式就是可以返回值的语句
表达式更倾向于自成一块,避免与上下文共享状态,相互依赖,因此具有更好的 隔离性。
从设计角度而言:
- 语句的作用就是服务于创建副作用的。
- 表达式的目的则是为了创造新值。
在函数式编程中。原则上表达式是不允许包含副作用的。
一切皆表达式。让开发者在设计业务时候,促进了避免创造副作用的逻辑设计。从而让程序变得更加安全。
都有值。并且可以将一个表达式作为组成其自身的一部分。
if-else 表达式
- 在if 作为表达式的时候,else 分支必须被考虑,
- 返回值类型是各个逻辑分支的相同类型或者公共父类型。
void & Void & Unit
- void : java中一个关键字。如果一个函数没有返回值,则需要用void 修饰
- Void : java的一个类,为了对应void,它继承Object,类型是final,构造方法是私有的,所以不能具有实例
- Unit : kotlin 中的一种类型。一个单例,可写为(),除了不代表任何意义之外,与其他常规类型并没有什么差别
Kotlin 中的?:
:,属于一种 elvis 运算符。类似三元运算符,maybeInt ?: 1
表示,如果变量maybeInt 不为空,返回maybeInt,否则返回1
枚举类
在枚举类中存在额外的方法或属性定义,必须强制加上分号。
enum class Day(val day: Int) {
MON(1),
TUR(2),
WEN(3),
THU(4),
FRI(5),
SAT(6),
SUN(7)
;//下面有额外的方法或者属性定义,必须加上分号
fun getDaNubmer(): Int {
return day
}
}
when 表达式
具体语法
- 类似switch,由when 关键字开始,用{}包含多个逻辑分支,每个分支由->连接,不再需要break,由上到下匹配,一直匹配完为止。否则执行else 分支,类似switch的default
- 每个逻辑分支具有返回值。最终整个when的表达式返回类型就是所有分支相同的返回类型,或公共父类型。
fun testWhen(a: Int) = when (a) {
1 -> println(2)
9 -> println(3)
2 -> 5
else -> when {
a > 7 -> 8
else -> 10
}
}
fun main() {
val fo = testWhen(9); //3
println(fo) //kotlin.Unit
val fo2 = testWhen(8);
println(fo2) //8
}
for 循环和范围表达式
for 循环遍历如下。
for (i: Int in 1..10) {
print(i) //12345678910
}
for (i in 1..10) {
print(i) //12345678910
}
for (i: Int in 1..10) print(i) //12345678910
for (i in 1 until 10) {
print(i) //123456789
}
for (i: Int in 1..10 step 2) {
print(i)//13579
}
for (i in 10 downTo 1 step 2) {
print(i) //108642
}
..
范围表达式, 1..10 表示从1 到10,除了整型的基本数据类型,实现了的Comparable接口的都可以。例如 “adc”..”xyz”- step 步长,step 2 表示隔一个取一个
- downTo 倒序
- until ,实现半开区间。
- in ,检查一个元素是否在另一个区间或者集合中
"kot" in "abd".."xyz" //true //等价于 "kot">="adb"&&"kot"<="xyz"
可变参数
使用 varags 关键字表示
fun printLetters(vararg letters: String, count: Int): Unit {
println("$count letter are ")
for (letter in letters) print(letter)
}
printLetters("1","3","t","e","d",count = 8)
不需要放到最后一个,参数的任意位置都行,如果不是最后一个,则后面的参数需要显示指定,例如 count=8,这种形式
可以使用*
来传入外部的变量作为可变参数的变量,例如
val letter= arrayOf("1","3","t","e","d")
printLetters(*letter,count = 8)
字符串的定义和操作
- 原生字符串,使用
""" """
包裹起来即可
val rawString="""
\n kotlin is awesome.
\n kotlin is a better java .
"""
print(rawString)
返回的结果如下
\n kotlin is awesome.
\n kotlin is a better java .
最终打印格式与在代码中所呈现的格式一致,并不会转义\n ,以及Unicode的转义字符
- 字符串模板
使用$ 直接进行拼接。例如 println("$count letter are ")
,count 是一个变量
提升了代码的紧凑性和可读性
-
字符串判等
- 结构相等
==
,判断对象内容是否相等 - 引用相等
===
,判断对象的引用是否相等。
搬运地址:
Kotlin 核心编程 - 水滴技术团队
既已览卷至此,何不品评一二: