kotlin函数(代码片段)

gengqiquan gengqiquan     2023-02-04     635

关键词:

尊重他人的劳动成果,转载请标明出处:https://blog.csdn.net/gengqiquan/article/details//87982480, 本文出自:【gengqiquan的博客】

infix标记法

  • 在以下情况下,也可以使用中缀符号调用函数:
  • 它们是成员函数或扩展函数-它们只有一个参数
  • 它们用infix关键字标记
// Define extension to Int
infix fun Int.shl(x: Int): Int  ...

// call extension function using infix notation
1 shl 2
// is the same as
1.shl(2)

可变参数

我们可以通过vararg修饰符来表明一个参数是可变参数来允许向函数传递可变数量的参数

fun <T> asList(vararg ts: T): List<T>  val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t) return result


val list = asList(1, 2, 3)

泛型函数

fun <T> singletonList(item: T): List<T>  // ...

尾部递归函数

Kotlin支持一种称为尾部l递归的函数式编程。这使得一些通常使用循环编写的算法可以使用递归函数编写,但不存在堆栈溢出的风险。当一个函数被标记为tailrec修饰符并满足所需的形式时,编译器会优化递归,而留下一个快速有效的基于循环的版本。

tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))

要符合tailrec修饰符的条件,函数必须调用自身作为它执行的最后一个操作。当递归调用后有更多的代码时,不能使用尾部递归,并且不能在try/catch/finally块中使用尾部递归。目前,只有JVM后端支持尾部递归。

高阶函数和lambda

高阶函数

高阶函数是以函数为参数(在高阶函数内部调用执行:类似代理)或返回函数的函数。

fun <T> lock(lock: Lock, body: () -> T): T  lock.lock()
try 
return body()

finally 
lock.unlock() 

body有一个函数类型:()->t,所以它应该是一个不带参数并返回t类型值的函数。它在try块内被调用,同时受锁保护,其结果由lock()函数返回。

要调用lock(),可以将另一个函数作为参数传递给它(请参见函数引用):

fun toBeSynchronized() = sharedResource.operation() val result = lock(lock, ::toBeSynchronized)

或者使用lambda

val result = lock(lock,  sharedResource.operation() )

lambda

  • lambda表达式总是由大括号包围,
  • 其参数(如果有)在->之前声明(参数类型可以省略),-正文在->之后(如果存在)。
  • 在Kotlin中,有一种约定,即如果函数的最后一个参数是函数,并且要将lambda表达式作为相应的参数传递,则可以把它写在括号后面

如果一个函数文本只有一个参数,则可以省略它的参数声明(连同->),然后用it来指代这个参数

strings.filter  it.length == 5 .sortBy  it .map  it.toUpperCase() 

1.1版本以后,未使用到的参数可以用下划线:“_”替代

map.forEach  _, value -> println("$value!") 

lambda表达式或匿名函数是一种“显式声明函数”,即未声明但可以作为表达式立即传递

max(strings,  a, b -> a.length < b.length )

等价于

fun compare(a: String, b: String): Boolean = a.length < b.length

max(strings, compare(a, b))

我们可以使用限定返回语法(return@方法名)从lambda显式返回值。否则,将隐式返回最后一个表达式的值。因此,以下两个片段是等效的:

ints.filter 
val shouldFilter = it > 0 shouldFilter

ints.filter 
val shouldFilter = it > 0 return@filter shouldFilter

匿名函数

如果可以通过上下文自动推断出函数返回值的类型,可以用匿名函数来替代

fun(x: Int, y: Int): Int  return x + y

匿名函数可以是一个表达式或者代码块
参数和返回类型的指定方式与常规函数相同,但如果可以从上下文推断参数类型,则可以忽略这些参数类型

注意,匿名函数参数总是在括号内传递。允许将函数保留在括号之外的简短语法仅适用于lambda表达式。
lambda表达式和匿名函数之间的另一个区别是非局部返回的行为。一个无标签返回语句总是从用fun关键字声明的函数返回。这意味着lambda表达式内部的返回将从封闭函数返回,而匿名函数内部的返回将从匿名函数本身返回。

闭包

lambda表达式或匿名函数(以及局部函数和对象表达式)可以访问其闭包,即在外部作用域中声明的变量。与Java不同,在封闭中使用到的外部变量可以被修改,如下函数可以通过编译并被正常执行:

var sum = 0
ints.filter  it > 0 .forEach 
sum += it 
print(sum)

带接收器的函数表达式

Kotlin提供了使用指定的接收器对象调用函数文本的能力。在函数文本的主体中,您可以对该接收器对象调用方法,而不需要任何附加限定符。这类似于扩展函数,它允许您访问函数体内部的接收器对象的成员

sum : Int.(other: Int) -> Int

1.sum(2)

匿名函数语法允许您直接指定函数文本的接收器类型。你可以用接收器声明一个函数类型的变量,稍后再使用它。

val sum = fun Int.(other: Int): Int = this + other

当可以从上下文推断接收器类型时,lambda表达式可以用作带有接收器函数表达式。

class HTML 
fun body()  ... 


fun html(init: HTML.() -> Unit): HTML 
val html = HTML() // create the receiver object
html.init() // pass the receiver object to the lambda
return html


html  // lambda with receiver begins here
body() // calling a method on the receiver object

JetBrains团队成员yole对接收器的函数表达式和扩展函数的区别给的回复是:带接收器的lambda可以传递给另一个函数,以便另一个函数调用它,并传递您可以在lambda主体中访问的接收器对象。扩展函数是一种只能直接调用的函数。

内联函数

使用高阶函数会带来一些运行时损害:每个函数都是一个对象,当它俘获一个闭包,即在函数体中访问的那些变量,会带来运行时内存分配(包括函数对象和类)和虚拟调用的额外开销。
但在许多情况下,通过内联lambda表达式可以消除这种开销。比如:

lock(l)  foo() 

编译器为了创建一个函数对象并调用,可能会生成以下代码

l.lock() try 
foo()
finally 
l.unlock()

为了让编译器做到这一点,我们可以在函数声明前加上inline修饰符

inline fun lock<T>(lock: Lock, body: () -> T): T  // ...

内联修饰符同时影响函数本身和传递给它的lambda:所有这些修饰符都将内联到调用代码处(类似Java早期的final方法)。内联可能会导致生成的代码增长,但如果我们以合理的方式进行(不要内联大函数),它将在性能上,尤其是在循环内的调用带来很大的提升。

如果你只希望传递给内联函数的某些lambda参数是内联的,则可以使用noinline修饰符标记其他的你不希望进行内联的函数参数

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit)  // ...

可内联lambda只能在inline函数内部调用或作为可内联参数传递,但是noinline参数可以以我们喜欢的任何方式进行操作:存储在字段中、传递等。
注意,如果一个内联函数没有不可内联的函数参数,也没有具体化的类型参数,编译器将发出警告,因为这样的函数不太可能是有益的(如果您确定需要内联,可以取消警告)。

在Kotlin中,我们只能使用普通的非限定返回来退出命名函数或匿名函数。这意味着要退出lambda,我们必须使用一个标签,并且lambda内禁止裸返回,因为lambda不能作为封闭函数返回

fun foo() 
ordinaryFunction 
return // ERROR: can not make `foo` return here 

但是,如果lambda传递给的函数是内联的,则返回也可以是内联的,因此如下代码是允许的:

fun foo()  
inlineFunction 
return // OK: the lambda is inlined 

注意,一些内联函数可能将传递给它们的lambda作为参数,而不是直接从函数体调用。比如将他从另一个执行上下文(本地对象或嵌套函数)调用,在这种情况下,非本地控制流也不允许出现在lambda中。要做到这一点,lambda参数需要用crossinline修饰符进行标记:

inline fun f(crossinline body: () -> Unit)  val f = object: Runnable 
override fun run() = body() 
// ...

break和continue在内联lambda中尚不可用,但是开发团队也计划支持它们。

具体化类型参数

有时我们需要将一个类型作为参数传递给函数,但是我们又不希望编写太复杂的获取代码,我们希望能这样简洁的声明一个需要类型的函数

treeNode.findParentOfType<MyTreeNode>()

我们提供reified修饰符来实现,比如上面的代码的声明如下:

inline fun <reified T> TreeNode.findParentOfType(): T?  
var p = parent
while (p != null && p !is T) 
p = p.parent

return p as T? 

我们用具体化的修饰符限定了类型参数,现在它可以在函数内部访问,就好像它是一个普通类一样。因为函数是内联的,所以不需要反射,普通的操作符就像!现在正在工作。此外,我们可以如上所述调用它:mytree.findparentoftype()。

虽然在许多情况下可能不需要反射,但我们仍然可以将其与一个已具体化的类型参数一起使用,比如:

inline fun <reified T> membersOf() = T::class.members
fun main(s: Array<String>)  println(membersOf<StringBuilder>().joinToString("\\n"))

注意:普通函数(未标记为inline)不能具有已具体化的参数。没有运行时表示形式的类型(例如:非具体化类型参数或虚设类型(如Nothing))不能用作具体化类型参数的参数。

内联属性(从1.1版本开始)

内联修饰符可用于没有支持字段的属性的访问器。你可以对单个属性访问器进行注释

val foo: Foo
inline get() = Foo()
var bar: Bar get() = ...
inline set(v)  ... 

你还可以注释整个属性,该属性将其两个访问器都标记为内联

inline var bar: Bar get() = ...
set(v)  ... 

在任何调用的地方,内联访问器将作为常规内联函数进行内联

协同程序

一些API执行耗时操作(如网络IO、文件IO、CPU或GPU密集型工作等),并要求调用者在完成之前进行阻塞。协程提供了一种避免阻塞线程的方法,并用一种更廉价、更可控的操作来代替它:暂停协程。
协同程序通过将复杂的内容放入库中来简化异步编程。程序的逻辑可以在协同程序中按顺序表达,底层库将为我们解决异步问题。库可以将用户代码的相关部分包装成回调、订阅相关事件、在不同线程(甚至不同的机器)上计划执行。它可以使代码保持简单,就好像是按顺序执行的一样。其他语言中可用的许多异步机制也可以使用Kotlin协程作为库实现。这包括来自C_和ECMAScript的异步/等待、来自Go的通道和选择以及来自C_和python的生成器/收益。

阻塞和协同的区别:

基本上,协同程序是可以挂起而不阻塞线程的计算。阻塞线程通常很昂贵,特别是在高负载下,因为只有相对较少的线程可以保持在周围,因此阻塞其中一个线程会导致一些重要的工作被延迟。
另一方面,协程悬挂几乎是自由的。不需要上下文切换或操作系统的任何其他参与。除此之外,暂停在很大程度上可以由用户库控制:作为库作者,我们可以决定暂停后会发生什么,并根据需要优化/记录/拦截。
另一个区别是协同程序不能被随机指令挂起,而只能在所谓的挂起点挂起,这些挂起点是对特殊标记函数的调用。

挂起函数

当我们调用用suspend修饰符标记的函数时,会发生暂停

suspend fun doSomething(foo: Foo): Bar  ...

这些函数被称为挂起函数,因为对它们的调用可能挂起协同程序(如果调用的结果已经可用,库可以决定不挂起而继续)。挂起函数可以采用与常规函数相同的方式获取参数和返回值,但只能从协程和其他挂起函数调用这些参数和返回值。实际上,要启动协同程序,必须至少有一个挂起函数,而且它通常是匿名的(即,它是一个挂起的lambda),比如:

fun <T> async(block: suspend () -> T)

这里,async()是一个常规函数(不是一个挂起函数),但是block参数有一个带有suspend修饰符的函数类型:suspend()->t。因此,当我们将lambda传递给async()时,它是一个挂起的lambda,并且我们可以调用暂停功能:

async 
doSomething(foo) 
...

同样的,await()可以是一个挂起的函数(因此也可以从异步块内调用),挂起协程,直到完成一些计算并返回其结果:

async  
...
val result = computation.await()
... 
    

注意,挂起的函数await()和dosomething()不能从像main()这样的常规函数调用:

fun main(args: Array<String>) 
doSomething() // ERROR: Suspending function called from a non-coroutine context

还要注意,挂起函数可以是虚拟的,当覆写它们时,必须指定挂起修饰符:

interface Base  suspend fun foo()

class Derived: Base 
override suspend fun foo()  ... 

@RestrictsSuspension 注解

XTension函数(和lambda)也可以标记为suspend,就像常规函数一样。这样就可以创建用户可以扩展的DSL和其他API。在某些情况下,库作者需要防止用户添加挂起协同程序的新方法。
为此,可以使用@restrictsuspension注解。当一个接收器类或接口R被@restrictsuspension注解时,所有挂起的扩展都需要委托给R的成员或其他扩展。由于扩展不能无限期地相互委托(程序不会终止),这就保证了通过调用R的成员(库的作者可以完全控制)来执行所有的暂停。

kotlin与java的比较:函数(代码片段)

1.基本函数java代码:publicvoidhello()System.out.print("Hello,World!");kotlin代码:funhello( 查看详情

kotlin扩展函数③(定义扩展文件|重命名扩展函数|kotlin标准库扩展函数)(代码片段)

文章目录一、定义扩展文件二、重命名扩展函数三、Kotlin标准库扩展函数一、定义扩展文件如果定义的扩展函数需要在多个Kotlin代码文件中使用,则需要在单独的Kotlin文件中定义,该文件被称为扩展文件;定义标准库函数的Standard.kt... 查看详情

kotlin函数篇(代码片段)

...理自:https://chiclaim.blog.csdn.net/article/details/88624808一、Kotlin函数的基本定义我们先来定义一个基本的函数:funmax(a:Int,b:Int):Intreturnif(a>b)aelseb解释一下fun关键字用来定义一个函数fun关键字后面是函数名(max)括号中间是函数... 查看详情

kotlin学习之函数(代码片段)

函数声明在kotlin中用关键字fun声明函数:fundouble(x:Int):Int其中Int是返回值类型,x指明参数类型是为Int函数用法通过传统方法调用函数:valresult=double(2)可以通过.调用成员函数Sample().foo()Sample()是Sample类的一个实例Infix符号... 查看详情

kotlin之标准函数的学习讲解(withapply)(代码片段)

1.Kotlin小课堂:标准函数Kotlin标准函数指的是Standard.kt文件中定义的函数,任何Kotlin代码都可以自由的调用标准函数.1.1with我们先从with函数学起,with函数接收两个参数,第一个参数可以是任意类型的对象,第二个参数是Lambda表达式.with函... 查看详情

kotlin总结(代码片段)

文章目录1、Kotlin介绍2、知识点总结2.1、变量2.2、函数(普通函数)2.3、内置类型2.4、类型同步2.5、表达式2.6、高阶函数3、Kotlin知识体系4、为什么要学习Kotlin5、Kotlin的工作原理6、Kotlin的优点7、输出“HelloWorld”原创不易... 查看详情

kotlin的标准函数和静态方法(代码片段)

...标准函数标准函数就是在Standard.kt文件中定义的函数,任何Kotlin代码都可以自由地调用所有的标准函数let函数就属于是一个标准函数,经常配合?.操作符来进行判空处理with函数with函数接收两个参数,第一参数可以是任何类型的对象,... 查看详情

初识kotlin之集合(代码片段)

Kotlin的集合是让我为之心动的地方,丰富的高阶函数帮助我们高效开发。今天介绍Kotlin的基础集合用法、获取集合元素的函数、过滤元素的函数、元素排序的函数、元素统计的函数、集合元素映射的函数、集合的交差并补集的函... 查看详情

kotlin作用域函数的使用经验(代码片段)

前言Kotlin给我们提供了很多Java没有的便利,作用域函数(ScopeFunction)就是Kotlin标准库里面的提供的一些让我们减少重复代码和提高可读性的一系列函数。下面结合我的使用经验来介绍一下Kotlin的作用域函数:是什么作用是... 查看详情

kotlin高阶函数(代码片段)

forEach便利funmain(args:Array<String>)varlist=listOf(1,2,3,4,5,6)list.forEach(::println)valnewList=arrayListOf<String>()--->1,2,3,4,5,6list.forEachnewList.add((it*2).toString())-- 查看详情

kotlin高阶函数(代码片段)

forEach便利funmain(args:Array<String>)varlist=listOf(1,2,3,4,5,6)list.forEach(::println)valnewList=arrayListOf<String>()--->1,2,3,4,5,6list.forEachnewList.add((it*2).toString())-- 查看详情

学习kotlin-扩展函数(代码片段)

...类的功能而无需接触它们的代码的函数。换句话说,Kotlin中的扩展函数允许我们通过添加新函数来扩展类的功能。该类不必属于我们(可以是第三方库),也不需要我们继承该类。举个很简单的例子来理解。funInt.... 查看详情

sword-为kotlin函数增加代理功能(代码片段)

简介Sword:一个可以给Kotlin函数增加代理的第三方库,基于KCP实现。Sword-为Kotlin函数增加代理功能(一)Sword-为Kotlin函数增加代理功能(二)前面两篇文章笔者记录了Sword的实现过程,本篇文章简单记录下如何使用Sword以及... 查看详情

kotlin常用的kotlin类②(枚举类|枚举类定义函数|密封类)(代码片段)

...普通函数2、枚举类定义构造函数三、密封类一、枚举类Kotlin中使用枚举类定义常量,枚举类定义格式如下:枚举常量都是枚举类的实例对象;enumclass枚举类类型名 枚举常量1, 枚举常量2, ...代码示例:enumclassGenderMALE,FEMALEfunmain()println(Ge... 查看详情

kotlin常用的kotlin类②(枚举类|枚举类定义函数|密封类)(代码片段)

...普通函数2、枚举类定义构造函数三、密封类一、枚举类Kotlin中使用枚举类定义常量,枚举类定义格式如下:枚举常量都是枚举类的实例对象;enumclass枚举类类型名 枚举常量1, 枚举常量2, ...代码示例:enumclassGenderMALE,FEMALEfunmain()println(Ge... 查看详情

kotlin函数(代码片段)

尊重他人的劳动成果,转载请标明出处:https://blog.csdn.net/gengqiquan/article/details//87982480,本文出自:【gengqiquan的博客】infix标记法在以下情况下,也可以使用中缀符号调用函数:它们是成员函数或扩展函数-它们... 查看详情

kotlin挂起函数基础(代码片段)

...c;比普通的函数多了suspend关键字。通过suspend关键字,Kotlin编译器就会特殊对待这个函数,将其转换成一个带有Callback的函数,这里的Callback就是Continuation接口。    例         CPS转换:susp 查看详情

kotlin小知识之高阶函数(代码片段)

文章目录高阶函数定义高阶函数函数类型高阶函数示例内联函数内联函数的作用内联函数的用法noinline与crossinline高阶函数定义高阶函数高阶函数和Lambda的关系是密不可分的.像接受Lambda参数的函数就可以称为具有函数式编程风格... 查看详情