Kotlin专题「六」:函数的声明和使用

前言:生命不息,奋斗不止,只要相信,只要坚持。

一、概述

  对于 Kotlin 中的函数来说,和 JavaScript 语言很像,对于 Java 来说它不是闭包的,这里主要讲函数的基本使用,包括函数的定义,参数,返回值等。同时介绍函数参数使用默认值和单表达式函数的使用。

二、函数的声明及使用

2.1 函数的声明

  Kotlin中函数的声明关键字为fun,定义的各式为:

可见修饰符  fun  函数名(参数名 : 参数类型) : 返回值类型{}

注意:默认为public可见修饰符,小括号()是必须存在的,即使没有参数的情况,大括号{}也是必须存在,即使没有函数体。如果函数没有返回值则可以省略返回值类型。例如:

    //参数numA,numB是Int类型,返回值是Int类型
    fun sum(numA: Int, numB: Int): Int {
        return numA + numB
    }

2.2 成员函数

成员函数是指在类或者对象的内部函数。声明一个类,在类中定义一个方法,这个方法就是这个类的成员函数。

class FunctionActivity {
    fun basic(){}
}

成员函数的使用和 Java 类似,调用成员函数使用点表示法:

    //成员函数直接调用
    basic()
    var str = basic()//有返回值

    创建类FunctionActivity的实例并调用basic()
    FunctionActivity().basic()
    var str2 = FunctionActivity().basic()//有返回值

有关类和重写成员的更多信息,请参考类和继承

三、函数的参数

3.1 有参数的函数定义

定义一个有参数的函数,使用Pascal法表示定义,即:name : type。其中参数是必须有显示的参数类型,并且参数与参数之间用,隔开。

    //参数numA,numB是Int类型,返回值是Int类型
    fun sum(numA: Int, numB: Int): Int {
        return numA + numB
    }

	Log.e(TAG, "带参数的函数:sum == ${sum(1, 2)}")

打印数据如下:

带参数的函数:sum == 3

3.2 默认参数

对于默认参数,即使函数中的参数具有默认值,这样可以使用该函数的时候,这减少了过载的数量,减少函数重载,灵活性更高。格式为:name : type = 默认值

    //默认参数
    fun defaultArgs(argsA: Int? = 0, argsB: Float = 1f, argsC: Boolean = false) {
        Log.e(TAG, "默认参数:argsA == $argsA | argsB == $argsB | argsC == $argsC")
    }
	
	//默认参数使用
    defaultArgs()
    defaultArgs(null, 20f)

打印数据如下:

默认参数:argsA == 0 | argsB == 1.0 | argsC == false

默认参数:argsA == null | argsB == 20.0 | argsC == false

如果函数中参数有默认值,可以对有默认值的参数不传递参数值,会使用默认值。

注意:当该函数是一个成员函数,并且该函数复写继承类中的方法,则该函数必须从签名中省略该函数的默认值,在你复写方法时,编辑器默认帮你实现。

open class A {
    open fun defaultArgsOver(i : Int = 10){}
}

class B : A() {
	//复写A中的方法
    override fun defaultArgsOver(a: Int) {
        super.defaultArgsOver(a)
        Log.e(TAG, "默认参数:argsOver == $a")
    }
}

//调用
B().defaultArgsOver(1)

打印数据如下:

默认参数:argsOver == 10

如果在默认参数之后的最后一个参数是一个lambda,你可以把它作为一个命名参数放在括号外面:

    fun play(weight: Int = 0, height: Int = 1, qux: () -> Unit) {
        //TODO
    }

    fun main(args: Array<String>) {
		play(1) { println("play") }
        play(qux = { println("play") })
        play { println("play") }
    }

3.3 命名参数

命名参数是指使用函数传递参数时,可以命名参数传入的格式是 参数名 = 参数值 这种形式的一个或多个参数。在函数有个多个参数(有默认值)时,如果只需要传一部分参数,而其他参数使用默认值时,那么我们只需要指定命名参数就可以了。

当你在函数调用中使用命名参数时,你可以自由地更改列出它们的顺序,如果你想使用它们的默认值,你可以完全不适用命名参数。

当调用这个函数时,你不需要命名它的所有参数:

    //命名参数
    fun nameArgs(str: String, num: Int = 0, float: Float = 1.0f, isTrue: Boolean = false) {
        Log.e(TAG, "命名参数:str == $str | num == $num  | float == $float  | isTrue == $isTrue")
    }

	//使用 `参数名 = 参数值` 的形式
	nameArgs("Kotlin", 1000, argsC = 20f, argsD = true)
	nameArgs("Andorid")//跳过所有参数赋值,将使用默认值

打印数据如下:

命名参数:argsA == Kotlin | argsB == 1000 | argsC == 20.0 | argsD == true
命名参数:argsA == Android | argsB == 0 | argsC == 1.0 | argsD == false

在JVM上,在调用 java 函数时不能使用命名参数语法,因为 java 字节码并不总是保留函数参数的名称。

3.2 可变参数

函数中的可变长参数使用vararg修饰,即当函数中的参数是不定个数并且是同一类型,则可以使用vararg修饰这个参数,被它修饰的参数相当于一个固定类型的数组。格式如下:

fun 函数名 (vararg 参数名 : 参数类型) : 返回值 {}

举个栗子:

    //可变参数
    fun varargArgs(vararg strs: String) {
        for (item in strs) {
            Log.e(TAG, "可变参数:strs == $item")
        }
    }
    
	//调用
	varargArgs("1", "2", "3", "4")

打印数据如下:

可变参数:strs == 1	 2	 3	 4

在函数内部,类型为 T 的可变参数可以看作为 T 的数组,也就是说,下面示例中的 arguments 变量的类型为 Array<out T>

	//vararg 修饰表示为可变参数
    fun <T> asList(vararg arguments: T): List<T> {
        val result = ArrayList<T>()
        for (arg in arguments) {
            result.add(arg)//arguments 是一个数组,类型为 T 的可变参数可以看作为 T 的数组
        }
        return result
    }

    fun main(args: Array<String>) {
		val list = asList(1, 2, 3, 4)
	}

只有一个参数可以标记为可变参数。如果可变参数不是列表中的最后一个参数,则可以使用命名参数语法传递一下参数的值,如果参数具有函数类型,则通过传递一个 lambda 外括号。

当我们调用一个可变参数函数时,我们可以一个一个传递参数,例如asList(1, 2, 3, 4),或者如果我们已经有一个数组,想要传递它的内容给函数,则使用扩展操作符(在数组前面加上 * 前缀):

	val nums = arrayOf(8, 9, 10)
	val lists = asList(-1, 0, *nums, 1, 21)

	println("lists == $lists")

那么 nums 就能将它的内容传递给函数,打印数据如下:

lists == [-1, 0, 8, 9, 10, 1, 21]

四、函数的返回值

在 Kotlin 中,函数的返回值可以分为两种:

  • Unit类型:    表示无返回值的情况,可以省略;
  • 其他类型:    显示其他指定的返回值,比如 Int,Float,String等。

4.1 Unit类型的返回值

函数的返回值是Unit类型的时候表示不需要返回值,可以省略,此类型对应于 Java 中的void类型。

    //无返回值, Unit修饰
    fun unitReturn(): Unit {
        //return Unit 或者 return 是可选
    }

    //无返回值
    fun unitReturn() {
    }

上面两个函数其实是等价的。

4.2 其他类型的返回值

其他类型的返回值说明该类型有返回值,并且返回值类型不能省略,返回值也不能省略。

    //返回值为String类型
    fun otherReturn(): String {
        return ""
    }

    //返回值为Int类型
    fun otherReturn2(): Int {
        return 0
    }

明确返回类型:具有 body 的函数必须始终显式指定返回类型,除非它打算让它们返回 Unit,在这种情况下,它是可选的,Kotlin 不会自动推断具有 body 的函数的返回类型,因为这样的函数在 body 中可能有复杂的控制流,并且返回类型对读者(有时甚至对编译器)并不明显。

4.3 单表达式函数

单表达式函数表示在函数具备返回一个表达式时,可以省略花括号{}直接用=赋值指定的body ,而函数的返回值是有编译器自动推算的。

	//1.完整形式
    fun double(num: Int): Int {
        return 2 * num
    }

	//2.简化后,省略大括号
	fun double(num: Int): Int = num * 2

	//3.省略显示返回类型
    fun double(num: Int) = num * 2

显式声明返回类型是可选的,可以由编译器来推断。

五、其他函数

5.1 Infix标识符

infix 关键字标记的函数也可以使用 infix 表示法调用(省略调用的点和括号)。infix 函数必须满足以下要求:

  • 它们必须是成员函数或扩展函数;
  • 它们必须只有一个参数;
  • 参数不能接受可变数量的参数,并且不能有默认值。

infix 函数调用的优先级低于算术运算符、类型强制转换和rangeTo运算符。以下表达式是等价的:

1 shl 2 + 3 等价于 1 shl (2 + 3)
0 until n * 2 等价于 0 until (n * 2)
xs union ys as Set<*> 等价于 xs union (ys as Set<*>)

另一方面,infix 函数调用的优先级要高于布尔运行符 &&||is--check 以及其他一些运算符。这些表达也是等价的:

a && b xor c 等价于 a && (b xor c)
c xor b in c 等价于 (a xor b) in c

注意:infix 函数始终需要制定接收方和参数。当你使用 infix 符号在当前接收器上调用一个方法时,你需要显式地使用 this,与常规的方法调用不同,它不能被忽略。这是确保明确解析所必需的。

class FunctionsActivity {
    infix fun add(str: String) {
        //TODO
    }

    fun addCurrentStr() {
        this add "Android" //正确
        add("Kotlin")   //正确
        //add "Java"   //错误, 必须指定接收方
    }
}

5.2 尾递归函数

Kotlin 支持一种称为尾部递归的函数式编程风格。这允许使用递归函数编写一些通常使用循环编写的算法,但没有堆栈溢出的风险。当一个函数被标记为 tailrec 修饰符并且满足要求的形式时,编译器会优化递归,留下一个快速高效的基于循环的版本替代:

val eps = 1E-10 // "good enough", could be 10^-15

tailrec fun findFixPoint(x: Double = 1.0): Double
        = if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))

这段代码计算余弦的不动点,它是一个数学常数,它只叫数学。cos 从1.0开始重复,直到结果不在变化,对于指定的 eps 精度,结果为0.7390851332151611。产生代码相当于这个更传统的风格:

val eps = 1E-10 // "good enough", could be 10^-15

private fun findFixPoint(): Double {
    var x = 1.0
    while (true) {
        val y = Math.cos(x)
        if (Math.abs(x - y) < eps) return x
        x = Math.cos(x)
    }
}

要符合 tailrec 修饰符的条件,函数必须在最后执行的操作中调用自己。当递归调用后还有更多代码时,不能使用尾部递归,而且不能在 try/catch/finally 块中使用尾部递归。目前 Kotlin 对 JVM 和 Kotlin/Native 都支持尾递归。

5.3 本地函数

Kotlin 支持本地函数,即一个函数在另一个函数内部:

fun dfs(graph: Graph) {
    fun dfs(current: Vertex, visited: MutableSet<Vertex>) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

    dfs(graph.vertices[0], HashSet())
}

局部函数可以访问外部函数的局部变量(即闭包),因此在上面的例子中,访问的可以是一个局部变量:

fun dfs(graph: Graph) {
    val visited = HashSet<Vertex>()
    fun dfs(current: Vertex) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v)
    }

    dfs(graph.vertices[0])
}

5.4 函数范围

在 Kotlin 中,函数可以在文件的顶层声明,这意味着你不需要创建一个类来保存函数,而在Java,c#或Scala等语言中需要这样做。除了顶级函数,Kotlin 函数也可以声明为本地函数,作为成员函数和扩展函数。

5.5 泛型函数

函数可以有泛型参数,在函数名前调用尖括号 <> 指定:

    fun <T> invite(item: T): List<T> {
        //TODO
    }

有关泛型函数的更多信息,请参考泛型函数

5.6 内联函数

有关内联函数的更多信息,请参考内联函数

5.7 扩展函数

有关扩展函数的更多信息,请参考扩展函数

5.8 高阶函数和lambdas

高阶函数和Lambdas也在各自的文章中讲解。请参考高阶函数和lambdas

源码地址:https://github.com/FollowExcellence/KotlinDemo-master

点关注,不迷路


好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才

我是suming,感谢各位的支持和认可,您的点赞、评论、收藏【一键三连】就是我创作的最大动力,我们下篇文章见!

如果本篇博客有任何错误,请批评指教,不胜感激 !

要想成为一个优秀的安卓开发者,这里有必须要掌握的知识架构,一步一步朝着自己的梦想前进!Keep Moving!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值