kotlin基础学习-7(扩展,函数式编程,序列相关)
2022-05-04 本文已影响0人
ftd黑马
-
定义扩展函数
扩展可以在不直接修改类定义的情况下增加类功能,扩展可以用于自定义类,也可以用于比如List,String,以及Kotlin标准库里的其他类。
和继承相似,扩展也能共享类行为,在无法接触某个类定义或者某个类没有使用open修饰符,导致无法继承的时候,扩展类就是增加类功能的最好选择。
//在addExt前加上类. ,这里就是扩展了String类的方法,这个方法是,字符串调用该函数返回该字符串+number个!
fun String.addExt(number:Int) = this + "!".repeat(number)
在main函数中调用
//因为下面新增了addExt函数,所以字符串都可以调用
println("abc".addExt(3))
- 超类定义扩展函数
//超类定义扩展函数
fun Any.easyPrint() = println()
在main函数中调用
//超类定义扩展函数
1.easyPrint()
"jljklljkl".easyPrint()
- 泛型扩展函数
//泛型扩展函数,这种写法非常的常见,在标准库函数中,let,also等都是这样写,使用泛型扩展,谁调用这个函数,T就是谁,并且返回当前的T
fun <T> T.easyPrint2() : T {
println(this)
return this
}
在main函数中调用
//泛型扩展函数
"asdf".easyPrint2().addExt(2).easyPrint2()
-
扩展属性
除了给类添加功能扩展函数外,还可以给类定义扩展属性
//给String类添加一个扩展属性,这个属性可以统计字符串里有多少个元音字母
val String.numVowel
get() = count{"aeiou".contains(it)}
在main函数中调用
"abcdefghijklmn".numVowel.easyPrint2()
- 可空类型扩展函数
//可空类型扩展函数,在扩展函数内解决可能出现的空值问题
fun String?.nullableWithDefault(default: String) = println(this?:default)
在main函数中调用
val nullable:String?= null
nullable.nullableWithDefault("abc")
-
infix关键字
infix关键字适用于有单个参数的扩展类函数,可以更"简洁"的调用。
如果一个函数定义了infix关键字,那么调用它时,调用者和函数之间的.以及参数的一对括号都可以不要。
infix fun String?.nullableWithDefault2(default: String) = println(this?:default)
在main函数中调用
//可空类型扩展函数
val nullable:String?= null
nullable.nullableWithDefault("abc")
//如果函数加了infix,那么上面这个可空类型扩展函数就可以这么写了
// mapOf("jack" to 18),map集合的写法点击to看源码,就是用infix关键字修饰
nullable nullableWithDefault2 "abc"
-
定义扩展文件
定义扩展文件,扩展函数需要在多个文件里面使用,可以将它定义在单独的文件,然后import。
新生成扩展文件Extention.kt,如下:
package com.ftd.extention
//定义扩展文件,扩展函数需要在多个文件里面使用,可以将它定义在单独的文件,然后import
fun <T> Iterable<T>.randomTake() = this.shuffled().first()
在别的文件中调用:
package com.example.myapplication
import com.ftd.extention.randomTake as randomTake2 //这里可以重命名
fun main() {
//定义扩展文件,扩展函数需要在多个文件里面使用,可以将它定义在单独的文件,然后import
val list = listOf("jack","tom","jason")
val set = setOf("java","android","python")
// list.shuffled().first()
//使用扩展文件里面的扩展函数
// list.randomTake()
//重命名扩展
list.randomTake2()
}
-
函数式编程
主要依赖于高阶函数(以函数为参数或者或返回函数)返回的数据,专用于处理各种数据集合,最经典的莫过于rxjava
函数类别
一个函数式应用通常由三大类函数构成:变换transfrom,过滤filter,合并combine。每类函数都针对集合数据类型设计,目标式产生一个最终结果。
函数式编程用到的函数生来都是可以组合的,也就是说,可以组合多个简单函数来构建复杂的计算行为。
1.变换函数
变换是函数式编程的第一大类函数,变换函数会遍历集合内容,用一个以值参形式传入的变换器函数,变换每一个元素,然后返回包含已修改元素的集合给链上的其他函数。
最常用的两个变换函数式map和flatMap
//map变换函数会遍历接收者集合,让变换器函数作用于集合里的各个元素,返回结果是包含已修改元素的集合,会作为链上下一个函数的输入。
val animals = listOf("tiger","cat","dog")
val babies = animals.map {
animal -> "A baby $animal"
}
//打印可以看到,原始集合并没有被修改,map变换函数和自己定义的变化器函数做完事情后返回的是一个新集合
println(animals)
println(babies)
//map返回的集合中的元素个数和输入集合必须一样,不过,返回的新集合的里的元素可以是不同类型的
val animalLength = animals.map { it.length }
println(animalLength)
//flatmap函数操作一个集合的集合,将其中多个集合的元素合并后返回一个包含所有元素的单一集合
val list = listOf(listOf(1,2,3), listOf(4,5,6)).flatMap { it }
println(list)//这里的打印结果是[1,2,3,4,5,6]
2.过滤函数filter
过滤是函数式编程的第二大类函数,过滤函数接收一个predicate函数,用它按给定条件检查接收者集合里的元素并给出true或者false的判定。
如果返回true,受检元素就会添加到过滤函数返回的新的集合里,如果是false,就会从新集合里移除。
//过滤集合里含有"J"的元素
val names = listOf("Jack","Rose","Jimmy","Tom")
val jNames= names.filter { it.contains("J") }
println(jNames) // 这里打印的是[Jack, Jimmy]
//Demo,组合使用map和filter找质数(只能被1和自身整除)
val numbers = listOf(4,22,7,39,53,11,55)
val numbersZhi = numbers.filter {
//这里的number就是集合里的每一个值,这里的第一个it指的是(2到nunber中的每一个值),第二个it指的是所有number取模后的值
number ->
(2 until number).map { number % it }.none { it == 0 }
}
println(numbersZhi)
3.合并函数
合并是函数式编程的第三大类函数,合并函数能将不同不同的集合合并成一个新集合,但是和flatmap不一样。
例如zip和fold
//zip合并函数来合并两个集合,返回一个包含键值对的新集合
val employee = listOf("jack","tom","rose")
val sizes = listOf(15,20,40)
val toMap = employee.zip(sizes).toMap()
println(toMap["rose"])
//fold函数用来合并值,这个合并函数接收一个初始的值,随后会根据匿名函数的结果更新
val fold = listOf(1, 2, 3, 4).fold(0) { total, number ->
total + number * 3
}
println(fold) // 这里的值 = 0+1*3 + 2*3 + 3*3 + 4*3
- 序列
package com.example.myapplication
/**
*
* list,set,map集合类型,这几个集合类型统称为及早集合,这些集合的任何一个实例在创建后,它要包含的元素都会被加入并且允许访问。
* 对应及早集合,kotlin还有另外一类集合:惰性集合。类似于类的惰性初始化,惰性集合类型的性能表现优异,尤其是用于包含大量元素的集合时,因为集合元素是按需产生的
*
*
*
*
* 序列
* kotlin有个内置惰性集合类型叫序列(Sequence),序列不会索引排序它的内容,也不记录元素数目。
* 在使用一个序列时,序列里的值可能由无限多,因为某个数据源能产生无限多个元素。
*
*
*/
fun Int.isPrime():Boolean{
(2 until this).map {
if (this % it == 0){
return false
}
}
return true
}
fun main() {
//demo,假设想产生头1000个质数。
//针对这个需求,我们并不知道1000个质数到哪里,所以假设0-5000里就包含1000个质数。
val take = (2..5000).toList().filter { it.isPrime() }.take(1000)
println(take.size) // 但是此时打印结果是669,不够1000个,所以这种写法还需要加while判断
//使用序列,这里的链式调用执行顺序应该不是顺序执行的,因为这个generateSequence方法和take方法都是Sequence重写过的,内部肯定是有关联。
val sequenceTake = generateSequence(2) { value ->
value + 1
}.filter { it.isPrime() }.take(1000)
println(sequenceTake.toList().size)//此时执行结果就是1000
}