1 常规函数

1.1 无参函数

fun main() {
myFun()
} fun myFun() {
println("myFun") // 打印: myFun
}

1.2 有参函数

1)常规调用

fun main() {
myFun("myFun") // 打印: myFun
} fun myFun(str: String) {
println(str)
}

2)形参指定默认值

fun main() {
myFun() // 打印: abc
} fun myFun(str: String = "abc") {
println(str)
}

3)实参指定变量名

fun main() {
myFun(b = 123, a = "abc") // 打印: abc123
} fun myFun(a: String, b: Int) {
println(a + b)
}

1.3 有返回值函数

1)常规调用

fun main() {
var c = add(3, 5)
println(c) // 打印: 8
} fun add(a: Int, b: Int): Int {
return a + b
}

​ 说明:对于无返回值类型函数,其返回类型为 Unit,如下,也可以省略不写。

fun myFun(str: String): Unit {
println(str)
}

2)单行函数体简化

​ 当函数内部只有一行代码时,可以简化如下。

fun main() {
var c = add(3, 5)
println(c) // 打印: 8
} fun add(a: Int, b: Int): Int = a + b

1.4 可变长参数函数(vararg)

1)常规调用

fun main() {
myFun("aa", "bb", "cc") // 打印: aa、bb、cc
} fun myFun(vararg parms: String) {
for (str in parms) {
println(str)
}
}

​ 说明:函数的可变长参数个数最多为 1 个。

2)使用数组接收可变长参数

fun main() {
myFun("aa", "bb", "cc") // 打印: 3
} fun myFun(vararg parms: String) {
var arr: Array<out String> = parms
println(arr.size)
}

3)将数组传给可变长参数函数

fun main() {
var arr: Array<String> = arrayOf("aa", "bb", "cc")
myFun(*arr) // 打印: 3
myFun("xx", *arr, "yy") // 打印: 5
} fun myFun(vararg parms: String) {
println(parms.size)
}

2 函数类型变量

2.1 函数类型变量

1)无参函数变量

fun test() {
println("test")
} fun main() {
var myFun: () -> Unit = ::test
myFun() // 打印: test
}

2)有参函数变量

fun test(a: Int, b: String): Unit {
println("test, $a, $b")
} fun main() {
var myFun: (Int, String) -> Unit = ::test
myFun(123, "abc") // 打印: test, 123, abc
}

3)有返回值函数变量

fun test(a: Int, b: Int): Int {
return a + b
} fun main() {
var myFun: (Int, Int) -> Int = ::test
println(myFun(3, 5)) // 打印: 8
}

2.2 匿名函数

​ 匿名函数即没有名字的函数,在声明函数变量时,可以指向一个匿名函数。

fun main() {
var myFun: (Int, Int) -> Int = fun(a: Int, b: Int): Int {
return a + b
}
println(myFun(3, 5)) // 打印: 8
}

​ 可以使用 Lambda 表达式简化如下。

fun main() {
var myFun: (Int, Int) -> Int = { a, b ->
a + b
}
println(myFun(3, 5)) // 打印: 8
}

3 内联函数(inline)

​ 内联函数是使用 inline 关键字修饰的函数,编译后会自动将函数体内的代码复制到调用处,以优化代码执行效率。

3.1 常规内联函数

​ Test.kt

fun main() {
myFun()
} inline fun myFun() {
println("内联函数")
}

​ 以上代码经过编译运行后,依次点击【Tools → Kotlin → Show Kotlin Bytecode】,生成字节码文件。

​ 再点击 DeCompile 按钮反编译字节码文件,会生成对应的 Java 文件。

public final class TestKt {
public static final void main() {
String var1 = "内联函数";
System.out.println(var1);
} public static void main(String[] var0) {
main();
} public static final void myFun() {
String var1 = "内联函数";
System.out.println(var1);
}
}

​ 说明:可以看到 myFun 函数里的代码被复制到调用处了。

3.2 带 return 的嵌套内联函数

1)return 不带标签

fun main() {
outFun {
println("inFun")
return // 等价于: return@main
}
println("main end") // 未打印
} inline fun outFun(inFun: () -> Unit) {
inFun()
println("outFun end") // 未打印
}

​ 运行结果如下。

inFun

​ "outFun end" 和 "main end" 未打印,这是因为内联函数会直接将 return 语句复制到 main 函数中。

2)return@标签

fun main() {
outFun {
println("inFun")
return@outFun
}
println("main end")
} inline fun outFun(inFun: () -> Unit) {
inFun()
println("outFun end")
}

​ 运行结果如下。

inFun
outFun end
main end

4 泛型函数

​ 泛型的类型检查只存在于编译阶段,在源代码编译之后,不会保留任何关于泛型类型的内容,即类型擦除。

4.1 简单泛型函数

1)单泛型参数

fun main() {
myFun(123) // 打印: 123
myFun("abc") // 打印: abc
myFun(true) // 打印: true
myFun(null) // 打印: null
} fun <T> myFun(param: T) {
println(param)
}

2)多泛型参数

fun main() {
var res: Boolean = myFun("abc", 123, true) // 打印: abc, 123
println(res) // 打印: true
} fun <R, T, S> myFun(a: T, b: S, c: R): R {
println("$a, $b")
return c
}

4.2 类中泛型函数

fun main() {
var c1: MyClass<String> = MyClass()
c1.myFun("abc") // 打印: abc
var c2: MyClass<Int> = MyClass()
c2.myFun(123) // 打印: 123
} class MyClass<T> {
fun myFun(a: T) {
println(a)
}
}

4.3 自动推断泛型类型

​ Kotlin 提供了下划线(_)运算符可以自动推断类型。

fun main() {
myFun<Int, _>()
} fun <S : Comparable<T>, T> myFun() {
println("test _")
}

​ Int 类和 Comparable 类的定义如下。由于 Int 继承了 Comparable,因此会自动推断 "_" 为 Int。

public interface Comparable<in T>
public class Int private constructor() : Number(), Comparable<Int>

4.4 抗变、协变和逆变

1)抗变

​ 如下,Int 是 Number 的子类,Number 引用可以指向 Int 对象,但是 Data 引用不能指向 Data 对象,Data 引用也不能指向 Data 对象,该现象称为抗变。

2)协变

​ 通过 out 关键字表示 Data 引用能指向 Data 对象,类似于 java 中的 "? extends Number"。

class Data<T>(var value: T)

fun main() {
var data1: Data<Int> = Data<Int>(1)
var data2: Data<out Number> = data1
println(data2.value) // 打印: 1
// data2.value = 1 // 编译错误, setter方法被限制
}

​ 说明:协变后,不能修改协变元素。使用 out 修饰的泛型不能用作函数的参数,对应类型的成员变量 setter 方法会被限制,只能当做一个生产者使用。

3)逆变

​ 通过 in 关键字表示 Data 引用能指向 Data 对象,类似于 java 中的 "? super Int"。

class Data<T>(var value: T)

fun main() {
var data1: Data<Number> = Data<Number>(1f)
var data2: Data<in Int> = data1
println(data2.value) // 打印: 1.0
data2.value = 2
var a: Any ?= data2.value // 只能用Any接收value
}

​ 说明:逆变后,只能使用 Any 接收逆变元素。使用 in 修饰的泛型不能用作函数的返回值,对应类型的成员变量 getter 方法会被限制,只能当做一个消费者使用。

4)通配

​ 在有些时候,我们可能并不在乎到底使用哪一个类型,我们希望一个变量可以接受任意类型的结果,而不是去定义某一个特定的上界或下界。在Kotlin 泛型中,星号(*)代表了一种特殊的类型投影,可以代表任意类型。

class Data<T>(var value: T)

fun main() {
var data1: Data<Int> = Data<Int>(1)
var data2: Data<*> = data1 // Data<*>等价于Data<out Any>
println(data2.value) // 打印: 1.0
// data2.value = 2 // 编译错误, setter方法被限制
var a: Any ?= data2.value // 只能用Any接收value
}

​ 说明:由于不确定具体类型,使用时只能是 Any 类型。

4.5 泛型的界

​ Kotlin 泛型中,可以为其指定上界。

1)单上界

class Data<T: Number>(var value: T)

fun main() {
var data1: Data<Int> = Data<Int>(1)
// var data1: Data<String> = Data<String>("abc") // 编译错误, 指定了上界为Number
var data2: Data<*> = data1 // Data<*>等价于Data<out Number>
println(data2.value) // 打印: 1.0
// data2.value = 2 // 编译错误, setter方法被限制
var a: Number = data2.value // 可以用Number接收value
}

2)多上界

open class A {}
interface B {} class Data<T>(var value: T) where T: A, T: B

4.6 具化类型参数(reified)

​ Kotlin 的内联(inline)函数可以使用 reified 关键字具化类型参数,允许在函数体内部检测泛型类型,因为这些类型信息会被编译器内嵌在调用点。但是,这只适用于内联函数,因为内联函数中的类型信息在编译时是可知的,并且实际类型会被编译到使用它们的地方。

​ 以下调用会编译报错。

​ 通过 inline 和 reified 修饰符,可以解决编译报错问题,如下。

inline fun<reified T> isType(value: Any) : Boolean {
return value is T
} fun main() {
println(isType<Int>("abc")) // 打印: false
println(isType<String>("abc")) // 打印: true
}

​ 声明:本文转自【Kotlin】函数

【Kotlin】函数的更多相关文章

  1. Kotlin 函数

    至于什么函数,在计算机里面就是一个密闭的执行程序的代码块(个人理解) 我们先来看看什么是函数 fun main(agrs : Array<String>) { println(" ...

  2. Kotlin函数与Lambda表达式深入

    Kotlin函数: 关于Kotlin函数在之前也一直在用,用fun来声明,回忆下: 下面再来整体对Kotlin的函数进行一个学习. 默认参数(default arguments): 先来定义一个函数: ...

  3. kotlin函数加强

    在之前已经接触过了kotlin的函数了,这里再次加强学习下它,下面开始吧! Kotlin函数编写规则: 对照函数来理解其写法: 演练巩固: ①.编写函数, 实现四则运算. 接着来实现其它三个运算: 然 ...

  4. Kotlin函数中默认参数

    Java不支持默认参数.但kotlin函数却可以 package loaderman.demo class Customer(var name:String ="name"){// ...

  5. kotlin函数api

    原 Kotlin学习(4)Lambda 2017年09月26日 21:00:03 gwt0425 阅读数:551   记住Lambda的本质,还是一个对象.和JS,Python等不同的是,Kotlin ...

  6. Kotlin 函数和函数表达式

    学习了kotlin 的控制. 其中最为强大的是各种控制可以直接作为表达式来使用. 那么在kotlin中,函数也是可以作为表达式来使用. 而且kotlin中函数的创建方式有很多方式. 第一种: 传统创建 ...

  7. Kotlin函数使用综述与显式返回类型分析

    位置参数与具名参数: 继续接着上一次https://www.cnblogs.com/webor2006/p/11498842.html的方法参数学习,再定义一个函数来说明具名参数的问题: 调用一下,先 ...

  8. kotlin函数和函数式表达式

    这次的写法可能有些怪异,但是如果熟悉java8的Lambda表达式的话其实理解起来很顺其自然[参考博客:http://www.cnblogs.com/webor2006/p/7705130.html] ...

  9. kotlin函数的参数和返回值

    fun main(arg: Array<String>) { val asList = asList(, , , , , ) println(asList) } fun <T> ...

  10. Kotlin语法(函数和lambda表达式)

    三.函数和lambda表达式 1. 函数声明 fun double(x: Int): Int { } 函数参数是用 Pascal 符号定义的 name:type.参数之间用逗号隔开,每个参数必须指明类 ...

随机推荐

  1. xml开发笔记(一):tinyXml2库介绍、编译和工程模板

    前言   Qt开发Xml相关技术,使用到tinyxml2库.   TinyXML   TinyXML是一个简单的.小的C++的XML解析器,可以集成到其他程序中.它是ROS的标准XML解析器.  最新 ...

  2. centos7安装桌面-GNOME

    CENTOS7安装桌面系统 GNOME桌面 # yum安装 # 更新已安装软件 yum upgrade -y # 安装额外yum源 yum install epel-release -y # 安装X ...

  3. 【LeetCode剑指offer#06】实现pow函数、计算x的平方根

    实现pow函数 实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn ). 示例 1: 输入:x = 2.00000, n = 10 输出:1024.00000 示例 2: 输入:x ...

  4. 【LeetCode哈希表#5】四数相加II(map)

    四数相加II 力扣题目链接(opens new window) 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + ...

  5. 名校AI课推荐 | MIT6.S191《深度学习导论》

    "连续开设5年,对新手友好.易于上手,参加课程的多数学生来自非计算机科学领域--" 推荐一门AI课程--MIT官方深度学习入门课程6.S191<深度学习导论(2022)> ...

  6. 【Azure 应用程序见解】在Azure门户中,创建App Service(应用服务)时,无法一起创建Application Insights的问题

    问题描述 创建Web应用时启用监视的问题.Azure管理员(Admin)用户下分配了 一个子用户.但是在使用子用户创建WEB应用的时候,启用 Application Insights 选项的 &quo ...

  7. 【Azure 应用服务】App Service / Function App 修改系统时区为中国时区的办法(Azure中所有服务的默认时间都为UTC时间,转换为北京时间需要+8小时)

    问题描述 在Azure的 App Service / Function App 服务中,如果是在Windows系统中,可以通过添加Application Setting来转换为中国时间(WEBSITE ...

  8. PHP项目&变量覆盖&反序列化&未授权访问&身份验证

    CNVD拿1day-验证&未授权-xhcms&Bosscms 此种漏洞由于没有什么关键函数,所以需要通过功能点去进行测试. Bosscms未授权访问 CNVD官网上搜索Bosscms未 ...

  9. CPN Tools 系统建模分析工具(持续更新)

    一直想把之前看有关CPN的文献资料做一个综合性的整理,所以最近花了些时间,把乌克兰敖德萨国家电信科学院交通运输部学院的讲义做一个翻译.本课程的翻译不具授权(如有侵权请及时联系,做删除处理) 本课程的标 ...

  10. 使用 Docker 部署 Next Terminal 轻量级堡垒机

    1)Next Terminal 介绍 官网:https://next-terminal.typesafe.cn/ GitHub:https://github.com/dushixiang/next-t ...