是时候整理kotlin的函数了!
Android中kotlin的函数
kotlin的函数
文中提到的 方法 函数 都是一个含义
kotlin中的函数分为普通函数,标准函数,静态函数,顶层函数,泛型函数,内联函数,外联函数,crossinline函数,扩展函数,中缀函数,高阶函数以及尾递归函数
普通函数
可以这样 像java
fun max(b: Int,a: Int):Int{
return a + b
}
可以这样
fun max(b: Int,a: Int):Int =a+b
也可以这样 省略返回值类型
fun max(b: Int,a: Int)=a+b
kotlin 中不定长参数用vararg表示 类似于Java方法参数中的…
fun mathod(vararg arguments: Any) {
for (i in arguments) {
println(i)
}
}
高阶函数
函数的参数可以是函数或返回值类型为函数的函数称为高阶函数(套娃警告⚠️)
是一种为了动态设置函数的功能
kotlin高阶函数和lambda是密不可分的 我们一般使用高阶函数都会传入一个lambda表达式作为参数
//一个简单的高阶函数
fun example(func:(String,Int)->Unit){
func("hello",321)
}
//另一个一个简单的高阶函数
fun example(): (String, Int) -> Unit {
return fun(s, i): Unit {}
}
kotlin中可以用变量来接收函数 也可以在函数的参数部分调用写好的函数 但是都要加上 ::双冒号
这表示把函数转换成函数类型的对象 kotlin和java有良好的互操性 任意java代码都可以转化成kotlin代码 反之一样 kotlin可以将函数作为函数的参数这件事的本质就是把函数转化为函数类型的对象(一种和函数有相同功能的对象) 所以才可以用变量指向函数
fun example2(string: String,int: Int,operation:(string: String,int: Int)->String): String {
return operation(string,int)
}
fun example(string: String,int: Int):String {
return string+int
}
//函数作为参数
val a = example2("asd",321,::example)
//函数赋值给对象
val c =::example2
匿名函数,lambda表达式
这两个函数很特别 因为他们不是函数是对象,准确的说是函数类型的对象
lambda底层是匿名函数
// 无参: val/var 变量名: () -> 返回值类型 = { 代码块 },
val a:() -> Int = { 10 }
// 有参: val/var 变量名: (变量类型...) -> 返回值类型 = { 参数1,参数2, ... -> 操作参数的代码 }
val b: (Int, Int) -> Int = {x, y -> x + y }
// 推导: val/var 变量名 = { 参数1: 类型, 参数2: 类型, ... -> 操作参数的代码 }
val c = { x: Int, y: Int -> x + y }
println(c(1,2)) ------> 3
inline内联函数,noinline外联函数,crossinline函数
在java中每次调用方法都会伴随一次栈帧的入栈出栈过程(有性能损耗 创建,入栈,出栈)可以把这些函数声明成内联函数(需要多次调用的函数 比如说工具类函数) 编译器会在需要调用内联函数的地方用内联函数的代码替代 可以节省性能的损耗 而缺点就是增加了所生成字节码的尺寸 因为每次调用内联函数都是把内联函数的代码拷贝过来
fun main() {
println(testInline(1, 2, 3))
println(testInline(4, 5, 6))
println(testInline(7, 8, 9, 10))
}
//内联函数
inline fun testInline(vararg int: Int): Int {
var sum = 0
for (i in int) {
sum += i
}
return sum
}
我们分两种情况 普通函数jvm会自行优化生成的字节码 我们不必声明成内联 类似于上面的例子 而以lambda为参数的函数 声明成内联函数确实可以提高性能
lambda本质就是匿名函数 每次调用都会生成新对象(用完即丢的对象) 调用几次还可以但是要放在循环体里就是个灾难
这句话的意思是高阶函数尽量生成为内联函数
需要注意的是 如果一个函数的参数存在lambda表达式 那么这个lambda也是inline的 下面这种情况会报错 因为不允许一个非内联函数去调用内联函数
inline fun method(inline:(Int,Int)->Int){
other(inline)
}
fun other(noinline:(Int,Int)->Int){
}
noinline是为了解决如何在内联函数体内使用非内联函数调用内联函数(lambda)的
不允许这么做的原因是因为在内联函数中的lambda不是一个真真切切的参数 为了提高效率会在编译时进行函数替换(这也是内联函数的特点)正是因为这样 作为内联函数参数的lambda不能作为参数传递给其他函数 所以需要把要传递的lambda声明成noinline 也就是说noinline所修饰的参数就还是对象(函数类型的对象) 对象就可以当作参数来传递了
inline fun method(noinline inline: (Int, Int) -> Int) {
other(inline)
}
fun other(noinline: (Int, Int) -> Int) {
}
与之相对的就是crossinline noline就是关闭inline优化 让lambda作为对象的存在 而cross就是加强了对inline函数的优化
局部加强inline优化-crossinline
下面是一个很简单对内联函数调用 是非局部返回 不会继续执行输出tkx
fun main() {
method {
println("world")
return
}
println("tkx")
}
inline fun method( inline: () -> Unit) {
println("hello")
run {
inline()
}
}
我们修改一下
变成局部返回 会继续执行输出tkx只跳出run方法
fun main() {
method {
println("world")
return
}
println("tkx")
}
inline fun method(crossinline inline: () -> Unit) {
println("hello")
run {
inline()
}
}
其实说白了 noinline crossinline都是为了处理使用inline函数时出现的特殊情况
标准函数
kotlin有个标准函数库装了一堆语法糖,任何的kotlin代码都可以随意的调用标准函数库 let,with ,apply,run also repeat 等
底层都是inline函数+lambda表达式
let函数 控制调用let的对象在一个作用域内 it就是指代这个对象 一般用来做空判断处理 就不用每个都 对象?. 这种形式了 在Android中还是挺方便的
mVideoPlayer?.let {
it.setVideoView(activity.video_view)
it.setControllerView(activity.video_controller_view)
it.setCurtainView(activity.video_curtain_view)
}
with函数 也差不对 接收一个对象 然后对它的操作就可以不用 . 了
with(mViewPager) {
setIndicatorSlideMode(IndicatorSlideMode.WORM)
setIndicatorSliderColor(
resources.getColor(R.color.gray_1),
resources.getColor(R.color.tkx_like)
)
setIndicatorStyle(1)
setIndicatorSliderRadius(
resources.getDimensionPixelOffset(R.dimen.dp_4),
resources.getDimensionPixelOffset(R.dimen.dp_4)
)
setLifecycleRegistry(lifecycleRegistry)
setAdapter(ViewBindingSampleAdapter(resources.getDimensionPixelOffset(R.dimen.dp_8)))
setInterval(5000)
setPageMargin(resources.getDimensionPixelOffset(R.dimen.dp_15))
setRevealWidth(resources.getDimensionPixelOffset(R.dimen.dp_10))
setPageStyle(int)
create(SettingUtil.getPicList(5))
}
run 函数 综合了 let 和 with
然后其他几个语法糖也类似 看一下就能懂
这个repeat语法糖有点甜 觉得很好用 但是到用的时候又想不起来(下次一定要用!)
repeat(2) {
list.add("321")
list.add("123")
}
//结果是321,132,321,123
扩展函数
可以给一个类加方法而不修改这个类
只需要把扩展的类或者接口名称,放到即将要添加的函数名前面。这个类或者名称就叫做接收者类型,类的名称与函数之间用"."调用连接。this指代的就是接收者对象,它可以访问扩展的这个类可访问的方法和属性(就是给类加个成员方法)
经常作为顶层函数
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//使用
text.isBlod()
}
//扩展函数 给字体加粗
fun TextView.isBlod() = this.apply {
paint.isFakeBoldText = true
}
}
顶层函数
没有定义在类中的方法
就是为了帮我们消除静态工具类,kotlin函数默认的修饰符是public可以放在顶层 在kotlin中我们不用像java一样为了面向对象而面向对象 直接调用方法就可以 方法的外部不需要有类的存在
package com.example.kotlindemo
import android.widget.TextView
/**
* @program: KotlinDemo
* @description:util类
**/
fun max(a: Int,b: Int)=a+b
fun TextView.isBlod() = this.apply {
paint.bgColor = R.color.colorAccent
}
不同的包引一下包名就能用了 不用写 class.
package com.example.kotlindemo
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//使用
text.isBlod()
max(5,6)
}
}
中缀函数
用infix修饰的扩展函数
它们必须是成员函数或扩展函数;
它们必须只有一个参数;
参数不能有默认值。
在kotlin中写map是这样的 所以这个to是干嘛的
val map = mapOf(1 to 2,2 to 3,3 to 4)
点进去后发现
/**
* 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)
然后我们在全局搜下infix
/** Shifts this value left by the [bitCount] number of bits. */
@kotlin.internal.InlineOnly
public inline infix fun shl(bitCount: Int): UInt = UInt(data shl bitCount)
/** Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with zeros. */
@kotlin.internal.InlineOnly
public inline infix fun shr(bitCount: Int): UInt = UInt(data ushr bitCount)
/** Performs a bitwise AND operation between the two values. */
@kotlin.internal.InlineOnly
public inline infix fun and(other: UInt): UInt = UInt(this.data and other.data)
/** Performs a bitwise OR operation between the two values. */
@kotlin.internal.InlineOnly
public inline infix fun or(other: UInt): UInt = UInt(this.data or other.data)
/** Performs a bitwise XOR operation between the two values. */
@kotlin.internal.InlineOnly
public inline infix fun xor(other: UInt): UInt = UInt(this.data xor other.data)
/** Inverts the bits in this value. */
@kotlin.internal.InlineOnly
public inline fun inv(): UInt = UInt(data.inv())
就是增加了代码的可读性 符合这种条件的函数可以省略方法调用的括号 类似于一个自定义的运算符的存在
我感觉没啥用 但是优雅永不过时
泛型函数
这个有点难搞 因为这个相比java的泛型厉害了(有空时候写一篇泛型)
首先为什么有泛型这个概念 我理解就是泛指的类型 因为现在做软件都是前后端分离 后端给接口 我们一般用map键值对的方式调用接口 然后数据上传和接收都会出现问题 没有一个标准来衡量应该传或者取什么类型 应该是都拿object来传 然后强转 泛型出来就好了 规定了这个数据应该是什么类型 比如年龄就传 住址就用 当然 泛型的作用远不止于此 泛型接口 泛型方法 泛型类。。。。。
本篇我们只讨论kotlin中的泛型函数
定义泛型函数 在函数名称前面添加“”,表示以T声明的参数(包括输入参数和输出参数),其参数类型必须在函数调用时指定
fun <T> appendString(tag: String, otherInfo: T?): String {
var str: String = "$tag:"
str = "$str${otherInfo.toString()}"
return str
}
fun main(args: Array<String>) {
var count =(0..3).random()
when (count ) {
0 -> println(appendString<String>("面对疾风吧", "0-20"))
1 -> println(appendString<Int>("快乐风男", 22))
else -> println(appendString<Double>("哈塞", 0.01))
}
}
静态函数
就是工具类哪都能调 kotlin有顶层方法了还需要静态方法干嘛。。。
companion object{
@JvmStatic
fun doAction(){
println("静态方法")
}
}
尾递归函数
尾递归函数:调用者竟是我自己
尾递归函数需要在 fun 前面添加 tailrec
实现的方式是循环 避免了栈溢出
计算阶乘
fun fact (n : Int) : Int{
if (n == 1) {
return n
} else {
return n * fact(n - 1)
}
}
尾递归实现
tailrec fun factRec(n: Int, b: Int = 1): Int =
if (n == 1) b else factRec(n - 1, b * n)
好用