《Kotlin 编程权威指南》 读书笔记
1 匿名函数
- 函数的定义 就是 把表达式或语句 放到一对 花括号里
{ 表达式 或 语句 }
- 函数类型
kotlin 中 参数 或 返回值 都是类型。 函数作为 ‘一等公民’ 也是一种类型。 使用 (参数)-> 返回类型 表示。
fun greetingFunction() : Strng //返回一个,
fun greetingFunction() : () ->Unit // 返回 一个 函数, 该函数 没有参数, 返回值 为 Unit
fun greetingFunction() : (Int) ->String// 返回 一个 函数, 该函数接收1个Int参数, 返回值String 的 函数。
-
Lambda 表达式
在Kotlin中, 我们把 匿名函数的定义 称为 Lambda表达式 。
lambda 函数体里 不能有 return 关键字
Lamdda表达式 默认以函数体 最后一行 代码的值,作为 返回值。 -
it 关键字
当 lambda 表达式 只定义了 一个参数时,可以用 it 来表示参数名。
fun IntRange.pickNum(function: (Int) -> Boolean): List<Int> {
val resultList = mutableListOf<Int>()
for (i in this) {
if (function(i)) resultList.add(i)
}
return resultList
}
println(list.pickNum{ it % 10 == 0 })
- 调用lambda 时 简略写法
如果函数只有一个参数,且参数为函数类型,调用时可以省略小括号
fun IntRange.pickNum(function: (Int) -> Boolean): List<Int> {
val resultList = mutableListOf<Int>()
for (i in this) {
if (function(i)) resultList.add(i)
}
return resultList
}
println(list.pickNum({ x: Int -> x % 10 == 0 }))
println(list.pickNum { x: Int -> x % 10 == 0 })
如果有多个参数,并且最后一个参数是函数类型,调用时可以将函数参数移到括号外
fun IntRange.pickNum(need :Int ,find : Int , function: (Int) -> Boolean): List<Int> {
val resultList = mutableListOf<Int>()
for (i in this) {
if (function(i)) resultList.add(i)
}
return resultList
}
println(list.pickNum(10,100,{ x: Int -> x % 10 == 0 }))
println(list.pickNum(10,100) { x: Int -> x % 10 == 0 })
- lambda 是闭包的
在kotlin中,匿名函数能 修改 和引用,定义在自己作用域之外的变量。
fun configureFun(): (String) -> String {
val value = "hospitals"
var number = 8
return { name: String ->
val year = 2019
number++
println("this is result $value number = $number")
"welcome to here ,$name ! (copyright $year)"
}
}
fun main() {
val greetingFun = configureFun()
println(greetingFun("老王"))
println(greetingFun("老李"))
println(greetingFun("老张"))
}
打印结果
this is result hospitals number = 9
welcome to here ,老王 ! (copyright 2019)
this is result hospitals number = 10
welcome to here ,老李 ! (copyright 2019)
this is result hospitals number = 11
welcome to here ,老张 ! (copyright 2019)
从结果中,可以发现。外部的变量 value number ,可以在lambda函数里直接调用 和修改。
2 null安全的操作符
- 定义 一个 返回值可以为空的方法
fun readLine() :String ?
-
String 是 String? 的子类
-
安全调用操作符 ?. 使用let 的 安全调用
fun readLine(): String? {
val number = listOf(1, 2, 3).shuffled().first()
if (number > 2) {
return "$number"
}
return null
}
val beverage = readLine()?.let {
if(it =="4"){
"${it.toInt() +3 }"
}else{
"3"
}
}
-
非空断言操作符 !!.
-
空合并操作符 ?:
当 readLine() 返回null时,beverage2 = “123”
val beverage2 = readLine() ?: "123"
3 标准库函数
- apply
源码:
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
参数是 this ,一般使用与 配置某个对象, 返回为 调用的对象本身。
val menuFile = File("xxxx").apply {
setReadable(false)
setWritable(true)
setExecutable(false)
}
- let
源码:
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
参数时 it ,返回 lambda表达的值,做空值处理。
fun formatGreeting(vipGuest: String?): String {
return vipGuest?.let { "welcome $it. go straight back" }
?: "welcome to the tavern. you will be seated shortly"
}
- run
源码:
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
和 apply 类似, 参数为this ,但 返回 lambda表达的值
run 常用语 函数的链式调用,能让代码逻辑更清晰
fun nameIsLong(name:String) = name.length >=20
fun playerCreatMessage( nameTooLong:Boolean) :String{
return if(nameTooLong){
"Name is too long,Please choose another name"
}else{
"Welcome to Adventurer"
}
}
//原代码
println(playerCreatMessage(nameIsLong("Polarise,Supreme Master of Netmask")))
//run 优化后
"Polarise,Supreme Master of Netmask"
.run(::nameIsLong)
.run(::playerCreatMessage)
.run(::println)
-
with
with 是 run 的变体,功能一样。 不推荐使用, 一般用run替代。 -
also
源码:
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
和let 类似,参数 it , 返回对象本身。
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
- takeIf
源码:
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}
功能类型 if else 。 参数 it , 返回 true 或者 null
val str = "Hello"
val caps = str.takeIf { it.isNotEmpty() }?.toUpperCase()
fun displaySubstringPosition(input: String, sub: String) {
input.indexOf(sub).takeIf { it >= 0 }?.let {
println("The substring $sub is found in $input.")
println("Its start position is $it.")
}
}
4 构造函数
- 主构造函数
class Cat( master: String, var name:String, val age :Int, color: Color = Color.BLACK){
}
需要注意的是主构造函数不包含任何代码,初始化程序块用于初始化代码
- 次级构造函数
class Cat( master: String, var name:String, val age :Int, color: Color = Color.BLACK){
constructor(master: String): this(master,age = 3, name ="喵喵")
}
我们可以在同一类中使用主构造函数和次级构造函数。通过在一个类中使用主和次级构造函数,次级构造函数需要授权给主构造函数,也就是次级构造函数会直接或者间接调用主构造函数。使用this()关键字对同一个类中的另一个构造函数进行授权。
构造函数 参数 可以是 var val ,也可以给初始值。
- 初始化顺序
1 主构造函数里声明的属性
2 类级别的属性赋值
3 init 初始代码块的属性赋值 和 函数调用
4 次级构造函数里的 属性赋值 和 函数调用
特殊说明: 2 3 条,init 初始化代码块 和 类级别的属性赋值, 顺序 取决于 定义的先后顺序。
5 Object 关键字
object 的使用 有3种方式: 对象声明 对象表达式 伴生对象
- 对象声明
单例模式
object Game{
const val TAG = "lsh"
init {
println("这是 一个 单例")
}
fun getSomething() : String {
return "给你点 惊喜!"
}
}
fun main(args: Array<String>) {
Game.getSomething()
var tag = Game.TAG
}
- 对象表达式
创建继承某个(或某些)类型的匿名类的对象,这些类型可以是接口 抽象类 普通类
button.setOnClickListener(object: View.OnClickListener {
override fun onClick(v: View?) {
Log.d(MainActivity::class.java.simpleName, "click button")
}
})
open class Man(_name: String) {
val name = _name
}
interface Flyable {
fun fly()
}
interface SuperHearing {
fun hearSubtleNoises()
}
fun main(args: Array<String>) {
val superMan = object : Man("Clark"),Flyable,SuperHearing{
override fun fly() {
println("i can fly")
}
override fun hearSubtleNoises() {
println(" hearSubtleNoises ")
}
}
}
对象表达式后面可以不跟任何类或者接口,这时它仅仅表示一个对象,封装一些数据和方法
val litCat = object {
val name = "喵喵"
fun eat(){
println("我 吃 鱼")
}
}
litCat.eat()
litCat.name
- 伴生对象
关键字 companion object , 可以理解 为 java 里的 静态变量 静态方法。
class MyActivity : AppCompatActivity() {
companion object {
const val TAG = "MyActivity"
@JvmStatic // 如果在 Java 代码中调用此方法,必须加上这个注解,才能编译出静态方法
fun start(context: Context) {
val starter = Intent(context, MyActivity::class.java)
context.startActivity(starter)
}
}
}