Kotlin 进阶语法 (1)

Collections 集合

主要集合: List, Set, Map

需创建可变集合时,添加mutable

val list1: MutableList<Int> = mutableListOf(1, 2, 3)
val list2: List<Int> = ListOf(1, 2, 3)  

集合函数

    val numbers = listOf(1, -2, 3, -4, 5, -6)
    // filter 截取
    val positives = numbers.filter { x -> x > 0 } 
    val negatives = numbers.filter { it < 0 }
    // map 映射
    val doubled = numbers.map { x -> x * 2 }
    val tripled = numbers.map { it * 3 }
    // any 存在
    val anyNegative = numbers.any { it < 0 }             
    // all 存在
    val allEven = numbers.all { it % 2 == 0 } 
    // none 存在
    val allEven2 = numbers.none { it % 2 == 1 } 
    // find 查找
    val words = listOf("Lets", "find", "something", "in", "collection", "somehow")  // 1
    val first = words.find { it.startsWith("some") }                                // 2
    val last = words.findLast { it.startsWith("some") }                             // 3
    val nothing = words.find { it.contains("nothing") }
    // first 查找
    val firstNum = numbers.first()
    val lastNum = numbers.last()
    val firstEven = numbers.first { it % 2 == 0 }        // 4
    val lastOdd = numbers.last { it % 2 != 0 }
    // 数量
    val totalCount = numbers.count()                     // 2
    val evenCount = numbers.count { it % 2 == 0 }
    
    data class Person(val name: String, val city: String, val phone: String) // 1

        val people = listOf(                                                     // 2
            Person("John", "Boston", "+1-888-123456"),
            Person("Sarah", "Munich", "+49-777-789123"),
            Person("Svyatoslav", "Saint-Petersburg", "+7-999-456789"),
            Person("Vasilisa", "Saint-Petersburg", "+7-999-123456"))

        val phoneBook = people.associateBy { it.phone }                          // 3
        val cityBook = people.associateBy(Person::phone, Person::city)           // 4
        val peopleCities = people.groupBy(Person::city, Person::name)            // 5
    // filter 分割

    val evenOdd = numbers.partition { it % 2 == 0 }  // ([-2, -4, -6], [1, 3, 5])
    val (positives2, negatives2) = numbers.partition { it > 0 }
    
    // flatMap 映射
    val tripled2 = numbers.flatMap { listOf(it, it, it) } // []
    println(numbers.flatMap { listOf(it, it *it) }) // [1, 1, -2, 4, 3, 9, -4, 16, 5, 25, -6, 36]

    // min and max functions 最大最小
    val empty = emptyList<Int>()
    println("Numbers: $numbers, min = ${numbers.min()} max = ${numbers.max()}") // 1
    println("Empty: $empty, min = ${empty.min()}, max = ${empty.max()}") // For empty collections both functions return null.
    // sorted 排序
    println("Soroted: ${ numbers.sorted()}")
    val inverted = numbers.sortedBy { -it }
    // 获取map的值
    val map = mapOf("key" to 42)
    val value1 = map["key"]
    val value2 = map["key2"]   // 返回null
    val value3: Int = map.getValue("key")  // 存在正常,不存在异常
    val mapWithDefault = map.withDefault { k -> k.length } // 设置默认值
    val value4 = mapWithDefault.getValue("key2")
    try {
       map.getValue("anotherKey")                              // 4
    } catch (e: NoSuchElementException) {
        println("Message: $e")
    }
    // zip 压缩

    val A = listOf("a", "b", "c")                  // 1
    val B = listOf(1, 2)                     // 1

    // zip 不能变长
    val resultPairs = A zip B                         // [(a, 1), (b, 2)],         
    val resultReduce = A.zip(B) { a, b -> "$a$b" }    //[a1, b2], 
    val resultlist = A.zip(B) { a, b -> listOf(a,b) } // [[a, 1], [b, 2]]
    // getOrElse 获取
    val list = listOf(0, 10, 20)
    println(list.getOrElse(1) { 42 })    
    println(list.getOrElse(10) { 42 })

    val map3 = mutableMapOf<String, Int?>()
    println(map3.getOrElse("x") { 1 })       
    map3["x"] = 3
    println(map3.getOrElse("x") { 1 })       
    map3["x"] = null
    println(map3.getOrElse("x") { 1 })

isInitialized

lateinit var a:String
//在使用的时候,a未必就一定初始化了,可以两种方式处理
//1,将a声明为String?并=null赋值,那么用的时候,使用a?.去操作,
//2,使用`isInitialized`判断是否初始化了
if(a.isInitialized) //do something

typealias类型别名

class PersonWithLongNameAndErrorSpell{}
//为了方便代码,可以声明类的别名
typealias Person = PersonWithLongNameAndErrorSpell

infix声明中缀函数

中缀函数必须满足的3个条件:

  1. 必须是成员函数或扩展函数;
  2. 只能有一个参数;
  3. 其参数不得接受可变数量的参数且不能有默认值。

即中缀函数为为成员函数或者扩展函数,并且只有一个参数、参数不能是可变参数且不能有默认值, 并且使用infix修饰。

在进行函数调用的时候可以使用中缀方式调用, 优点是具有更强的可读性。

//中缀函数,作用于同类型的数据对象,而且函数声明在类之中
class DemoInt(value:Int){
  var mInt = value
	//中缀函数声明
	infix fun sumInfix(sumTo:DemoInt){
    return this.mInt+sumTo.mInt
  }
}
//普通的sum函数是
fun sum(a:Int,b:Int) = a+b
//DemoInt使用中缀函数的求和
val a = DemoInt(2)
val b = DemoInt(3)
a sumInfix b//就会得到和一个求和,

kotlin标准库中的to函数 就是一个中缀函数

/**
 * Creates a tuple of type [Pair] from this and [that].
 *
 * This can be useful for creating [Map] literals with less noise, for example:
 * @sample samples.collections.Maps.Instantiation.mapFromPairs
 */
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

operator操作符函数重载

//类似中缀函数,在对应类中声明函数,
class DemoInt(value:Int){
  var mInt = value
  //操作符重载,这里是重载了 + 号
	operator fun plus(demo:DemoInt){
  		println("$mInt , ${demo.mInt}")
	}
}
//如此对于DemoInt对象就可以用+操作
val a = DemoInt(2)
val b = DemoInt(3)
a+b//这里就是调用了内部重载的plus函数

函数式编程

1. 函数声明为变量来使用
fun sum(a:Int,b:Int) = a+b
//定义一个不可变量的函数对象,f类型为一个函数表达式,也就是结果Int类型,然后=一个定义好的函数 sum,使用::前缀引用
val f:(Int,Int)->Int = ::sum//也可以使用var,但是val更为合理
//如此就可以在其他地方使用sum函数,用f表示
val result = f(2,3)
 
//可以结合lambda
val f2:(Int,Int)->Int={a,b->
  a+b
}
//进一步
val f3 = {a:Int,b:Int->
  a+b
}
2. 类可以继承函数表达式
//类继承函数,就必须实现一个invoke函数,这时候被继承的表达式,类似于接口作用
class Div:(Int,Double)->Double{
  override fun invoke(p1: Int, p2: Double): Double {
            return (p1 / p2)
        }
}
3. 函数表达式作为其他函数的形参
//定义一个函数,表达式(Int,Int)->Int
fun sum(a:Int,b:Int) = a+b
//使用函数形参,也就是理解为,传递来一种算法,
fun callSum(function:(Int,Int)->Int){
  val result = function(2,3)//这里只知道使用function计算2,3,并不知道怎么计算,根据传参来决定算法
}
callSum(sum)//传递进一个求和算法,那么就会计算2+3,以此类推
//传参可以是匿名函数,//匿名的(Int,Int)->Int函数,
callSum({a,b->
  a+b+20
}
)

这里要注意的,如果函数只有一个参数,可以不用a ->在lambda中,会有一个默认形参it,而且调用处可不用()

val square:(Int)->Int = {it*it}
//匿名函数,求面积,一个参数
fun round(function:(Int)->Int){
  println(function(3.14))//这里就是,接收3.14作为参数的fun函数计算
}
//匿名函数的调用方式,传递进去一种算法
round(fun(x:Int) = x*x)
//进一步,lambda 调用,这里传参的为匿名函数(Int)->Int,等效于上面的square
round{it*it}

接收者receiver

//有点类似于扩展函数,可以声明在对象类之外,其他任何需要的地方都行,然后就能用target的对象调用,像它自身的函数一样
class Person{
  val name="固定的名字"
}
//声明receiver,这时候的lambda中,没有it参数,而是this,指代当前Person类,在调用时候,就会指引对象
val rec:Person.()->String = {"名字是 $name"}
//然后调用处,有Person的对象就行
val p = Person()
p.rec()

在函数内声明receiver

class Chicken(color:String){
  val eggs = mutableListOf<Chicken>()
  //function为定义的receiver函数类型,声明?可空,并=null为默认值
  fun produce(color:String,function:(Chicken.()->Unit)?=null){
    val c = Chicken("red")
    eggs.add(c)
    if(function!=null){
      //调用传来的函数算法
      c.function()
    }
  }
}
 
//调用处
val cc = Chicken("yellow")
val rec:Chicken.()->Unit = { "这里没有it参数,只有this,所以$color" }
//1、普通调用
cc.produce("green",rec)
//2、匿名函数
cc.produce("red",{"this就是这个对象$this"})
//3、lambda调用,因为是一个参数的表达式,且为最后一个形参
cc.produce("pink"){"这就是lambda的魅力 $this"}

内联函数inline

方法调用流程
调用一个方法是一个压栈和出栈的过程,调用方法时将栈针压入方法栈,然后执行方法体,方法结束时将栈针出栈,这个压栈和出栈的过程会耗费资源,这个过程中传递形参也会耗费资源。

为什么需要inline?
有些简单的方法会被频繁调用,什么叫简单的方法呢,举个例子:

fun <T> check(lock: Lock, body: () -> T): T {
        lock.lock()
        try {
            return body()
        } finally {
            lock.unlock()
        }
    }

这个check方法的方法体中,不会将它的形参再传递给其他方法。我们调用一下 check 方法:

  check(l, {"我是lambda方法体"})//l是一个Lock对象

对于编译器来说,调用 check 方法就要将参数l和 lambda 表达式 {"我是 lambda方法体"} 进行传递,还要将 check 方法进行压栈出栈处理,这个过程就会耗费资源。

如果我们把 check 方法删除,直接执行 check 方法的方法体:

  l.lock()
  try {
      return "我是lambda方法体"
  } finally {
      l.unlock()
  }

这样做的效果和调用 check方法是一样的,而且不需要压栈出栈了,但是代码是写给人看的,这样写明显产生了代码坏味道,老司机会告诉你,这几行代码需要抽成一个方法,避免多处调用产生冗余代码。于是你就老老实实把这几行代码抽成了 check方法,那么如上所述,一旦这个方法被频繁调用,压栈出栈将会带来性能问题。针对这个问题,kotlin 引入了 inline 关键字。我们在 check 方法前加上 inline关键字:

inline fun <T> check(lock: Lock, body: () -> T): T {
        lock.lock()
        try {
            return body()
        } finally {
            lock.unlock()
        }
    }

然后我们再调用 check 方法,编译器就会在编译期帮我们进行优化:
将我们写的代码

  check(l, {"我是lambda方法体"})//l是一个Lock对象

换成

   l.lock()
   try {
       return "我是lambda方法体"
   } finally {
       l.unlock()
   }

也就是说 inline 关键字实际上增加了代码量,但是提升了性能,而且增加的代码量是在编译期执行的,对程序可读性不会造成影响。

其它
如果 check 方法中的参数需要传递给其他非 inline 方法:

inline fun <T> check(lock: Lock, body: () -> T): T {
            lock.lock()
            try {
                otherCheck(body)//会报错
                return body()
            } finally {
                lock.unlock()
            }
    }

    fun <T> otherCheck(body: ()-> T){

    }

那么调用 otherCheck 是会报错的,因为 check 方法中的形参 body 现在已经 inline 了,不是一个函数对象了,也就不能作为一个参数传递了,除非在 body 参数前加上 noinline 关键字。

run()函数

//run函数是Any?的扩展函数,可以用于配合?.操作可null的对象
var str:String?=null
//调用方不确定str是否null,但是还有一些函数需要非null的str作为参数
fun callNoNull(str:String){
  //do somethins
}
//此时调用可以用?避免NPE,也想非NUll时候运行callNoNull函数,结合run函数
str?.run{callNoNull(this)}
		?.run{
      callNoNull1(this)
      callNoNull2(this)
    }
//如此,str为null,则不会调用callNoNull,若非null,则可调用run内的函数,并且,整个表达式返回最后一个run内的函数的返回结果

let()函数

//类似于run()函数,也可以过滤null对象,不同的是,它使用的是普通的函数形参,而run使用的是receiver函数形式
str?.let{callNoNull(it)}
//let函数使用的it,而不是this,同上,可以多次调用,也可以内部多次调用其他函数。并返回最后运算的结果类型

apply()函数

//顾名思义,就是将对象应用于某些操作和算法,这里内部的每个代码块都是拿到的同一个对象,而且不判断null
maybeNull?.apply {
    firstFunction(this)
    secondFunction(this)
    memberPropertyA = memberPropertyB + memberFunctionA()
}?.memberFunctionB()

also()函数

//类似于apply,只是在lambda内,使用it而不是this引用

thkeIf()与thkeUnless()

//takeIf表示摘取仅仅满足条件的数据返回,可能为?,而takeUnless表示提取排除某些条件之外的数据,并返回,也可能为?,所以可以结合配置let函数
array.takeIf{it>12}?.let{it*it}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值