Kotlin中的函数

无论函数还是方法我们这里统称函数,Koltin中的函数要比Java中丰富的多,我们这篇文章来了解下Kotlin中的各类函数。

内联函数

Android开发中,打印信息一般我们会用到Log类,Log中每个方法我们都要传两个参数,第一个tag参数在Kotlin中我们可以像下面封装一下,这样就只需要传一个参数。

inline fun <reified T> T.debug(log:Any)
{
    Log.d(T::class.simpleName, log.toString())
}

我们发现它可以通过泛型参数 T 来获取到T的具体类型,并且拿到它的类名——当然,如果你愿意,你甚至可以调用它的构造方法来构造一个对象出来——为什么 Kotlin 可以做到呢?因为这段代码是 inline 的,最终编译时是要编译到调用它的代码块中,这时候T的类型实际上是确定的,因而 Kotlin 通过 reified 这个关键字告诉编译器,T 这个参数可不只是个摆设,我要把它当实际类型来用。

在高阶函数前增加inline注解可以指定函数內联,inline 标记即影响函数本身也影响传递进来的 lambda 函数:所有的这些都将被关联到调用点。内联可能会引起生成代码增长,但我们可以合理的解决它(不要内联太大的函数)。也可以使用noinline来指定某些函数不进行內联。

inline fun foo(inlined: () -> Uint, @noinline notInlined: () -> Unit) {
    //...
}

可以内联的 lambda 表达式只能在内联函数内部调用或者作为可内联的参数传递, 但是 noinline 的可以以任何我们喜欢的方式操作:存储在字段中、传送它等等。

单表达式函数

如果一个函数的函数体只有一个表达式,函数体可以直接写在 “=”之后,也就是这样:

fun double(x: Int): Int = x * 2

再例如下面这样:

fun eval(expr: Expr): Double = when(expr) {
    is Expr.Const -> expr.number
    is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
    Expr.NotANumber -> Double.NaN
    // the `else` clause is not required because we've covered all the cases
}

这里使用了when关键字,类似于java中的switch,但比之更强大。

匿名函数

/**
 * 匿名函数,没有名字,其他语法和常规函数类似
 *
 * 声明一个匿名函数,这里用表达式来表示函数体,也就是单表达式函数
 */
var test3= fun(x:Int,y:Int):Int=x+y
/**
 * 声明一个匿名函数,这里用代码块来表示函数体
 */
var test4= fun(x:Int,y:Int):Int {
    return  x+y
}
/**
 * 声明一个匿名函数,当返回值类型可以推断出,可以省略
 */
var test5= fun(x:Int,y:Int)=x+y

fun main(args: Array<String>) {

    println(test3(1,2))
    println(test4(1,2))
    println(test5(1,2))
}

高阶函数与lambda表达式

高阶函数就是可以接受函数作为参数或返回一个函数的函数。比如 lock() 就是一个很好的例子

fun lock<T>(lock: Lock, body: () -> T ) : T {
    lock.lock()
    try {
        return body()
    }
    finally {
        lock.unlock()
    }
}

在 kotlin 中并且许多类似语言都有一个约定,就是如果最后一个参数是函数,可以定义在括号外面:

val result = lock(lock, { sharedResource.operation() })
//等同于
lock (lock) {
    sharedResource.operation()
}

Lambda表达式是定义匿名函数的简单形式,Lambda表达式与匿名函数有哪些区别:

  • 是否指定返回值的类型

Lambda表达式返回值的类型通过以自动推断得到
匿名函数的返回值类型必须手动指定,如果未指定返回值类型,默认返回值类型为Unit

  • return的行为

Lambda 表达式内的 return 将会从包含这个Lambda表达式的函数中返回
匿名函数内的 return 只会从匿名函数本身返

尾递归函数

什么是尾递归?尾递归就是递归调用的那行代码是函数的最后一行代码。Koltin中对尾递归进行了优化,使用tailrec修饰的函数称为尾递归函数,避免内存溢出的风险。

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

这段代码计算的是数学上的余弦不动点。Math.cos 从 1.0 开始不断重复,直到值不变为止,结果是 0.7390851332151607 这段代码和下面的是等效的:

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

实际上就是用while迭代的方式代替了函数的递归调用。

局部函数

局部函数,就是定义在函数体的内函数。

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

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

局部函数可以访问外部函数中的局部变量, 因此, 在上面的例子中, visited 可以定义为一个局部变量。

静态方法

有两种方式

  • companion object 伴生对象与类对应,一个类只有一个伴生对象
class Main2Activity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
    }

    companion object {

        fun getStatic() {

        }

        fun getStaticString(string: String): String? {
            return null
        }
    }
}
  • object 使用object关键字声明一种特殊的类,这个类只有一个实例,因此看起来整个类就好像是一个对象一样。
object MoreImageUtils {
    fun filesToMultipartBodyParts(files: List<File>): List<MultipartBody.Part>? {
        return null;
    }
}

这里把类声明时的class关键字改成了object,这个类里面的成员默认都是static的。

顶层函数

在Kotlin中,函数能够被定义为top_level,即包下的函数。函数的定义方式不同,其作用域也不相同。当然,函数的作用域还与修饰符相关。

// file name: example.kt
package foo
private fun foo() {} // 只在 example.kt 文件内可访问
internal val baz = 6 // 在同一个模块(module)内可以访问
fun testFun() {} //默认修饰符public,被其修饰的在任何位置都能访问

扩展函数

Kotlin的扩展函数功能使得我们可以为现有的类添加新的函数,实现某一具体功能 。
扩展函数是静态解析的,并未对原类添加函数或属性,对类本身没有任何影响。

fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT){

    Toast.makeText(this, message, duration)
.show()
}
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)

Kotlin提供了一些扩展函数和顶层函数。

  • let函数

let默认当前这个对象作为闭包的it参数,返回值是函数里面最后一行,或者指定return

fun testLet(): Int {
    // fun <T, R> T.let(f: (T) -> R): R { f(this)}
    "testLet".let {
        println(it)
        println(it)
        println(it)
        return 1
    }
}
  • apply函数

调用某对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象。

ArrayList<String>().apply {
    add("testApply")
    add("testApply")
    add("testApply")
    println("this = " + this)
}.let { println(it) }

// 运行结果
// this = [testApply, testApply, testApply]
// [testApply, testApply, testApply]
  • with函数

with函数返回是最后一行,然后可以直接调用对象的方法,感觉像是let和apply的结合。

fun testWith() {
    // fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
    with(ArrayList<String>()) {
        add("testWith")
        add("testWith")
        add("testWith")
        println("this = " + this)
    }.let { println(it) }
}
// 运行结果
// this = [testWith, testWith, testWith]
// kotlin.Unit
  • run

run函数和apply函数很像,只不过run函数是使用最后一行的返回,apply返回当前自己的对象。

fun testRun() {
    // fun <T, R> T.run(f: T.() -> R): R = f()
    "testRun".run {
        println("this = " + this)
    }.let { println(it) }
}
// 运行结果
// this = testRun
// kotlin.Unit
  • repeat

没什么好讲的,重复做某件事情,他的源码如下:

@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    for (index in 0..times - 1) {
        action(index)
    }
}
  • lazy

lazy延迟运算,当第一次访问时,调用相应的初始化函数,例如:

fun readFromDb(): String = ...
val lazyString = lazy { readFromDb() }

val string = lazyString.value

当第一次使用 lazyString时, lazy 闭包会调用。一般用在单例模式。

  • use

use 用在 Java 上的 try-with-resources 表达式上, 例如:

val input = Files.newInputStream(Paths.get("input.txt"))
val byte = input.use({ input.read() })

use 无论如何都会将 input close, 避免了写复杂的 try-catch-finally 代码。

最后的小例:

val long = 100L
val db2 = 100.db

//扩展属性
private val  Int.db: BigDecimal
    get() = BigDecimal(this)


data class Money(val amount:BigDecimal, val currency: String)
//运算符重载
operator fun Money.plus(money: Money) =
        if (currency == money.currency)
        {
            Money(amount+money.amount, currency)
        }else {
            throw IllegalArgumentException("We're have a problem here!")
        }

//任何具有单个参数的扩展函数都可以使用中缀符
infix fun Int.percentOf(money:Money) = money.amount.multiply(BigDecimal(this)).divide(BigDecimal(100))

fun main(args: Array<String>) {

    val res = 7.percentOf(Money(3.db, "$"))

    //中缀调用
    7 percentOf Money(2.db, "$")

    println(res)

    val costs = Money(100.db, "$").plus(Money(200.db, "$"))
    println(costs.amount)

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值