kotlin高阶函数

news2024/12/20 14:38:50

kotlin高阶函数

函数式API:一个函数的入参数为Lambda表达式的函数就是函数式api
例子:

public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}

上面这段函数: 首先这个函数是一个泛型函数
泛型函数的定义:就是我们在写完一个函数后,只知道一个总的类型,而这个总的类型下有很多继承了这个总类型的类,在返回时我们不知道这个函数具体返回哪个子类,这时我们就可以在这个函数前面加一个泛型,泛型中放这些子类的基类,上面的filter方法也可以看作 Iterable的扩展方法

例子:

// A为基类
public class A{

}
public class  B extends A{

}

public class  C extends A{

}	

// 我们不知道下面这个方法具体返回A,B,C到底哪一个类型时,因为类B和C继承自A,这时我们就可以将这个返回类型指定为基类型A

public static <A> test(){

}

下面我写kotlin的写法

class ReportV2Controller : Controller(){
}

和上面java一样有多个类继承自Controller()这个类,kotlin中继承都是以函数的形式,因为这样写直观的表现了自类具体会调用基类的哪个构造函数

	public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}		

上面的filter方法是迭代集合Iterable,但是在kotlin中这个Iterable是一个被inline修饰的内联函数

内联函数的出现的原因:
1.首先在kotlin中有高阶函数的原因,因为高阶函数的传参可以是一个函数,而且出参也可以为一个函数

// 下面这段代码原作者为csdn: Mr YiRan
fun num1AndNum2(num1:Int,num2:Int,operator:(Int,Int)->Int):Int{
    val result=operator(num1,num2)
    return result
}

上面这段代码高阶函数num1AndNum2入参为两个Int类型与一个函数类型operator,这个operator又接收两个int类型的参数,这个operator返回类型为int,operator返回的类型int同时也是num1AndNum2高阶函数的返回类型,同时num1AndNum2函数的入参数,也可以当作函数operator的入参

fun num1AndNum2(num1:Int,num2:Int,operator:(Int,Int)->Int):Int{
    val result=operator(num1,num2)
    return result
}

// 定义两个具体的运算函数
fun plus(num1: Int,num2: Int):Int{
return num1+num2   
}
fun minus(num1: Int,num2: Int):Int{
    return num1-num2
}


fun main(){
    val num1=100
    val num2=80
    // 这是一个函数引用方式的写法,表示将plus()和minus()函数作为参数传递给num1AndNum2()函数
    val result1= num1AndNum2(num1,num2,::plus)
    val result2 = num1AndNum2(num1, num2, ::minus)
    println("result1 is $result1")
    println("result2 is $result2")
}



fun main(){
val num1=100
val num2=80
// 上面的函数引用写法的调用可以改为Lambda表达式的方式来调用高阶函数
val result1=num1AndNum2(num1,num2){ n1,n2 ->
n1+n2
}
val result2=num1AndNum2(num1,num2){ n1,n2 ->
n1-n2
}
println("result1 is $result1")
println("result2 is $result2")
}




Lambda表达式的优点与缺点

优点 :
如果一个抽象类或者接口中只有一个方法,我们又不想实例化这个类的对象就想调用这个方法,而且也不想将这个类中的方法标记为静态的方法,就可以用匿名内部类的方式类写,但是这样写代码很不美观,所以就简化成Lambda表达式的方式来写

缺点:
Lambda表达式的方式,系统会默认实例化一个匿名内部类的方式,就会造成额外的内存开销与cpu性能损耗
因为就算高阶函数中的传参写成Lambda表达式的方式,其内部运行顺序也为先实例化最外层类的对象,然后通过对象去引用当前这个对象的方法,将当前对象的方法存入方法区进行压栈然后再实例化一个Lambda表达式的匿名内部类,再通过这个对象去引用这个匿名内部类中的方法,再进行压榨

kotlin是如何解决这种因为传入Lambda表达式写法的性能开销的

内联函数

在定义高阶函数时加上inline关键字,就表示此高阶函数是一个内联函数

inline fun num1AndNum2(num1:Int,num2:Int,operation:(Int,Int)->Int):Int{
val result=operation(num1,num2)
return result
}

如何消除 Lambda表达式额外开销
简单来说就是:在调用传参为 Lambda表达式的高阶函数时,将 Lambda表达式的函数体直接拷贝在调用参数为 Lambda表达式的高阶函数后方
示例
在这里插入图片描述
在这里插入图片描述
1.首先将 Lambda表达式的入参拷贝到函数类型参数的参数入参列表中
2.其次再将内联函数的方法体拷贝到调用该高阶函数的地方
3.总结为:将实例化匿名内部类调用方法压栈改为方法入参数拷贝和方法体拷贝的过程

下面给一个返回值是一个函数的高阶函数
示例

/**

  • 定义一个返回值是函数的高阶函数
  • @param name 入场
  • @return 返回一个函数或者lambda
    */
    fun highFunction2(name:String):(Int) -> Int{
    if (name == “A”){
    // 这里返回一个函数引用,意思调用returnFun函数
    // 上面的highFunction2函数入参为两个一个String类型的name,还有一个入参是一个参数为一个int类型的Lambda表达式
    // 如果想调用returnFun函数那highFunction2函数传入的Lambda表达式入参类,数量与顺序必须完全与函数returnFun一致
    return ::returnFun
    }
    //返回lambda
    return {a -> a + 10}
    }

/**

  • 作为高阶函数的返回函数
    */
    fun returnFun(a:Int):Int{
    return a * 100
    }
//使用高阶函数
val res = highFunction2("A")
println(res(20)) //打印2000
val res2 = highFunction2("B")
println(res2(20)) //打印30

扩展函数

fun main() {
    open class Shape

    class Rectangle: Shape()

    fun Shape.getName() = "Shape"

    fun Rectangle.getName() = "Rectangle"

    fun printClassName(s: Shape) {
        println(s.getName())
    }    

    printClassName(Rectangle())

上面函数的最终返回结果是打印"Shape",因为虽然printClassName的函数的传入参数是Rectangle()函数,但是最终会调用Shape类的getName()函数,这是因为最终返回的类型取决于printClassName函数传入参数s的类型

成员函数与扩展函数

如果一个类定义有一个成员函数与一个扩展函数,而这两个函数又有相同的接收者类型、 相同的名字,并且都适用给定的参数,这种情况总是取成员函数
示例

fun main() {
    class Example {
        fun printFunctionType() { println("Class method") }
    }

    fun Example.printFunctionType() { println("Extension function") }

    Example().printFunctionType()
}

这里最后会输出:“Class method”,因为是以类的成员函数为准的,优先级为先成员函数
如果是扩展类型的重载,那么以传入参数为准
由于静态调用扩展方法是在编译时执行,因此,如果父类和子类都扩展了同名的一个扩展方法,引用类型均为父类的情况下,会调用父类的扩展方法
示例

fun main() {
    class Example {
        fun printFunctionType() { println("Class method") }
    }

    fun Example.printFunctionType(i: Int) { println("Extension function") }

    Example().printFunctionType(1)
}

最后输出结果为:Extension function

top-level函数

不依赖于任何类的静态函数,经Kotlin编译成Java文件后,成为:<静态函数所在的文件名> + "Kt"的Java类的静态成员函数。如果原kotlin文件的文件名首字母为小写时,转换成大写。
示例

/** joinsample.kt */
package com.example.kotlin

import java.lang.StringBuilder

fun <T> joinToString(
    collection: Collection<T>,
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
): String {
    val result = StringBuilder(prefix)
    for ((index, element) in collection.withIndex()) {
        if (index > 0) {
            result.append(separator)
        }

        result.append(element)
    }
    result.append(postfix)

    return result.toString()
}
import com.example.kotlin.JoinsampleKt;
import java.util.Arrays;
import java.util.List;

public class JavaSample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello", "world");
        JoinsampleKt.joinToString(list, ", ", "", "");
    }
}

扩展属性

由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。这就是为什么扩展属性不能有初始化器。他们的行为只能由显式提供的 getters/setters 定义。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/788831.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nginx配置server_name讲解

文章目录 1.Nginx配置中没有server_name会怎样&#xff1f;2.Nginx配置server_name的匹配规则3.正则表达式规则 1.Nginx配置中没有server_name会怎样&#xff1f; 此时Nginx会自动设置成 server_name ""; 它不会匹配任何域名&#xff0c;导致Nginx会优先将HTTP请求交…

2023年7月第3周大模型荟萃

2023年7月第3周大模型荟萃 2023.7.25版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 1、华为发布大模型时代 AI 存储新品 7 月 14 日华为在深圳发布了大模型时代 AI 存储新品&#xff0c;为基础模型训练、行业模型训练&#xff0c;细分场…

连锁反应开始了!Linux 发行版迎新变化!

任何企业都有合法权利捍卫其模型和产品。撇开大量不真正了解开源许可证如何工作的人不谈&#xff0c;我们的印象是&#xff0c;有很多人觉得仅仅因为这是Linux&#xff0c;他们就有某种权利免费获得它。但事实上&#xff0c;他们没有。这不是自由软件中的“自由”的意思&#x…

【VCS】(5)Fast RTL-level Verification

Fast RTL-level Verification General Coding GuidlinesLab --- simprofile$display() 输出彩色内容 前面的内容都是在说怎样进行仿真和验证&#xff0c;即如何使用 VCS 。 但是&#xff0c;仿真和验证是不是也有所讲究&#xff1f; 有没有一些标准来衡量设计代码和验证代码的质…

面向初学者的APP开发教程:开始你的编程之旅

不管你是已经在 APP开发行业中工作了很长时间&#xff0c;还是正在学习该领域的知识&#xff0c;都有必要开始学习如何编写一个应用程序。对于初学者来说&#xff0c;编写应用程序的第一步是使用 HTML和 CSS构建一个漂亮的UI。 一旦你学会了这些基本技能&#xff0c;你就可以开…

全志F1C200S嵌入式驱动开发(解决spi加载过慢的问题)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 之前的几个章节当中,我们陆续解决了spi-nor驱动的问题、uboot支持spi-nor的问题。按道理来说,下面要做的应该就是用uboot的loady命令把kernel、dtb、rootfs这些文件下载到ddr,然…

【Milvus】记录一次基于milvus-backup做的Milvus备份与恢复

文章目录 环境代码准备备份构建/运行验证 恢复遇到的问题 环境 milvus&#xff1a;v2.2.4 go&#xff1a;1.20.2 darwin/amd64 milvus-backup&#xff1a;v0.2.2 代码准备 https://github.com/zilliztech/milvus-backup/releases 如果你的milvus是2.2.9版本及以上&#xf…

SAP CAP篇十一:支持Media Object:图片、附件等

文章目录 本系列此前的文章官方文档详细修改更新数据库修改Annotation使其显示在Object Page上 运行结果Fiori Object Page上的Attachment Facet选择完文件后的UI效果前台与后台的交互 对应代码及branch 本系列此前的文章 SAP CAP篇一: 快速创建一个Service&#xff0c;基于Ja…

软件测试如何做到充分性测试?

目录 1 提前介入测试 2 测试分析&#xff0c;测试用例设计 3 测试用例评审 4 严格按照测试用例执行测试 5 分解需求 6 交叉测试 7 重点功能要及时跟踪进行测试充分性分析 做软件测试要想保质保量&#xff0c;就要做到测试充分&#xff0c;什么是测试充分&#xff0c;就是…

将数组和减半的最少操作次数(力扣)

将数组和减半的最少操作次数 题目描述思路测试代码复杂度测试结果 题目描述 给你一个正整数数组 nums 。每一次操作中&#xff0c;你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。&#xff08;注意&#xff0c;在后续操作中你可以对减半过的数继续执行操作&#xf…

(笔记)深度理解-主成分分析PCA

主成分分析 PCA(Principal Component Analysis)&#xff0c;即主成分分析方法&#xff0c;是一种使用最广泛的数据降维算法。PCA的主要思想是将n维特征映射到k维上&#xff0c;这k维是全新的正交特征也被称为主成分&#xff0c;是在原有n维特征的基础上重新构造出来的k维特征。…

蛋白质分子结构设计

paper read 1 Created by: 银晗 张 Created time: May 27, 2023 3:47 PM Tags: Product 补充了解蛋白质的生物学知识学习一下Diffusion的原理 &#x1f4a1; Method & Innovations Framework Summary: first deep learning models to perform antibody sequence-stru…

随笔--更改已经启动中的容器的配置文件

文章目录 docker 容器的配置信息地址修改文件映射 docker 容器的配置信息地址 # 一般在 sudo su cd /cd /var/lib/docker/containers/{容器id}/ # 查看容器的id,CONTAINER ID就是容器id的前部分 docker ps修改文件映射 进入容器的配置文件位置一般包含这些文件 # 先stop容器…

0基础学习VR全景平台篇 第69篇:VR直播-如何设置广告

直播间可以插入轮播广告&#xff0c;并且支持外链跳转&#xff0c;能够有效地提升VR直播活动的转化率。 1、点击&#xff0c;添加广告 2、广告图展现形式分为两种&#xff1a;普通广告和全屏广告&#xff0c;普通广告在非全屏播放的直播间显示&#xff0c;全屏广告在全屏播放的…

特征选择策略:为检测乳腺癌生物标志物寻找新出口

内容一览&#xff1a;microRNA&#xff08;小分子核糖核酸&#xff09;是一类短小的单链非编码 RNA 转录体。这些分子在多种恶性肿瘤中呈现失控性生长&#xff0c;因此近年来被诸多研究确定为确诊癌症的可靠的生物标志物 (biomarker)。在多种病理分析中&#xff0c;差异表达分析…

在 “小小容器” WasmEdge 里运行小小羊驼 llama 2

昨天&#xff0c;特斯拉前 AI 总监、OpenAI 联合创始人 Andrej Karpathy 开源了 llama2.c 。 只用 500 行纯 C 语言就能训练和推理 llama 2 模型的框架&#xff0c;没有任何繁杂的 python 依赖。这个项目一推出就受到大家的追捧&#xff0c;24 小时内 GitHub 收获 4000 颗星&am…

AI学习笔记三:编写检测的yolov5测试代码

若该文为原创文章&#xff0c;转载请注明原文出处。 通过detect.py代码测试通过后&#xff0c;阅读detect.py代码发现&#xff0c;有些难以看懂&#xff0c;看得有点蒙蒙的&#xff0c; 所以编写了一个简单的测试程序。 代码如下&#xff1a; import cv2 import numpy as np…

工业自动化编程与数字图像处理技术

编程是计算机领域的基础技能&#xff0c;对于从事软件开发和工程的人来说至关重要。在工业自动化领域&#xff0c;C/C仍然是主流的编程语言&#xff0c;特别是用于工业界面(GUI)编程。工业界面是供车间操作员使用的&#xff0c;使用诸如Halcon或OpenCV等软件单独无法完成项目&a…

mysql 第八章

1.主从复制 主服务器&#xff1a; 从服务器&#xff1a; 检测结果&#xff1a; 2.读写分离 amoeba 机器&#xff1a; 客户端机器&#xff1a; 检测结果&#xff1a; 3.总结 在企业应用中&#xff0c;业务通常数据量都比较大。单台 mysql 在安全性、 高可用性、高并发方面都&am…

走好职业生涯第一步 中科驭数2023校招生培训“芯星计划” 落幕

校招生作为公司发展的新鲜血液&#xff0c;是公司在人才储备和人才梯队建设上的重要投资。近日&#xff0c;中科驭数在北京、武汉两地组织开展了2023年校招生培训项目——“芯星计划”&#xff0c;旨在帮助2023届校招新员工快速了解公司文化、融入驭数团队&#xff0c;顺利迈过…