Kotlin函数用法全面分析
前言:)
现代编程语言最有趣的 10 大特性
1 Pipeline operator
2 Pattern matching
3 Reactive (Rx) programming build in the language
4 Lambda
5 Destructuring
6 Cascade operator
7 If expressions
8 Try expressions
9 Automatic currying
10 Method extensions
11 Optional chaining
12 DSL
正文:)
1.声明
Java
public String function(String param){
return param.concat("!");
}
Kotlin
fun function(param: String): String {
return "$param!"
}
Kotlin
- 修饰符 默认public
- 返回值 后置位声明(推导可省)
- 参数 类型后置
- 函数体 fun关键字声明 单表达式可省
{}
,return
fun function(param: String) = "$param!"
2.顶层函数
函数是一等公民
不在类中,直接声明在kt文件最外层
3.局部函数
与Java不同:作用域不同,变量名可重
fun outter() {
var a:String
fun inner() {
var a:String
}
}
4.匿名函数
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return // 局部返回到匿名函数的调用者,即 forEach 循环
print(value)
})
赋值给变量
val anonymous = fun(s: String): Int { return s.toIntOrNull() ?: 0 }
5.缺省参数
Java里面有重载
Kotlin采用了默认参数值
fun read(a: Array = emptyArray(), b: Int = 0, c: Int = a.size) { /*……*/
}
//1不关心顺序 2有缺省值
val read = read(c = 1, b = 2)
6.var在fun中不被允许 ,构造函数除外
class classA(var str: String) {}
7.解构声明
val map1 = mapOf("1" to 1, "2" to 2)
map1.forEach { key, value -> println("$key&$value!") }
//缺省值
map1.forEach { _, value -> println("$value!") }
//数组解构
val (param, value) = "param=car=c".split("=")
val (c, car) = "param=car=c".split("=").reversed()
8.高阶函数(函数作为参数传递)
- 声明参数为"函数类型"
- 函数类型表示法 (x: Int, y: Int) -> Point
fun funA1(fun1: (i: Int) -> String) : String {
return fun1(1)
}
9.声明函数类型别名
typealias Alias = (Int) -> String
fun test(a: Alias) {}
10.Lambdas 表达式
- 相当于匿名函数, 刻画了映射关系, 无需为其起名字
- Lambdas 表达式是花括号括起来的代码块
- 参数声明可省, ->箭头可省
- 当 lambda 函数的输入只有一个参数时,可以省略掉参数定义,用 it 代替
- Lambdas函数的返回值为 最后一个表达式的值 或者显式return返回
val lambda = { x: Int, y: Int -> {x+y} }
val lambda = { x: Int, y: Int -> x+y }
11.Lambdas作为函数实参传递
fun test(func: (Int) -> String) {
func(1)
}
fun main() {
val lambda = { x: Int -> x.toString() }
test(lambda)
test({ x: Int -> x.toString() })
}
Lambda 表达式 是 最后一个实参 ,可以拿到括号外面
test() { x: Int -> x.toString() }
//近一步省略 ()
test /*()*/ { x: Int -> x.toString() }
//近一步省略 x:Int -> 省略成 it (单个参数的隐式名称)
test { it.toString() }
拖尾 Lambda 表达式
fun foo(i: Int = 0, lambda: () -> Unit) { /*……*/
}
val foo1 = foo(lambda = { println("hello") })
val foo2 = foo(1) { println("hello") }
val foo3 = foo { println("hello") }
Lambda 表达式的返回值为 最后一个表达式的值 或者显式返回
test {
val a = it + 1
return@test a.toString()
}
test tag@{
val a = it + 1
return@tag a.toString()
}
Lambda 与高阶
fun safeRun(runnable: () -> Unit) {
try {
runnable()
} catch (t: Throwable) {
t.printStackTrace()
}
}
fun testNormalSafeRun() {
safeRun {
System.out.println("testNormalSafeRun")
}
}
12.函数类型实例化
//仅是声明
fun isOdd2(x: Int) = x % 2 != 0
//已是实例
val lambdaIsOdd = { x: Int -> x % 2 != 0 }
//已是实例
val isOdd1 = fun(x: Int) = x % 2 != 0
fun testLdb() {
val numbers = listOf(1, 2, 3)
//实例化,传递引用
numbers.filter(::isOdd2)
//实例化的直接传递
numbers.filter(lambdaIsOdd)
numbers.filter(isOdd1)
numbers.filter { x: Int -> x % 2 != 0 }
}
13.LINQ-风格
函数式编程
比起指令式编程,函数式编程更加强调程序执行的结果而非执行的过程
倡导利用若干简单的执行单元让计算结果不断渐进
逐层推导复杂的运算,而不是设计一个复杂的执行过程。
data class DataA(val name: String, val age: Int)
fun testLINQ() {
var list = ArrayList()
repeat(10) {
list.add(DataA("gaom$it", 10 + it))
}
//容器的内置扩展函数
list
.filter { it.age >= 15 }
.sortedByDescending { it.age }
.map { it.age }
.joinToString(separator = "-", prefix = "(", postfix = ")")
.run { print(this) }
//>>> (19-18-17-16-15)
//容器的 asSequence
println(list
.asSequence()
.map { it.name }
.filter { it.startsWith("gaom") }
.toList())
//>>> [gaom0, gaom1, gaom2, gaom3, gaom4, gaom5, gaom6, gaom7, gaom8, gaom9]
// 找到第一个平方大于3的数,输出它的平方
println(listOf(1, 2, 3, 4, 5)
.asSequence()
.map { it * it }
.find { it > 3 })
//>>> 4
}
14.闭包
非纯函数 访问修改了其作用域外的变量
Lambda 表达式或者匿名函数(以及局部函数和对象表达式)
可以访问其 闭包 ,即在外部作用域中声明的变量。
fun testClosure() {
var sum = 0
val ints = listOf(1, 2, 3, 4, 5)
ints
.filter { it > 0 }
.forEach {
//请注意 这里传递给forEach方法的是整个{}也就是Lambda也就是匿名方法 ,
//1 首先java不支持方法做为参数传递
//2 其次java匿名内部类访问局部变量需要将局部变量final
sum += it
}
print(sum)//>>>15
}
15.可变参数
fun funVararg(vararg strings: String) { /*……*/
}
val funVararg1 = funVararg("a", "b", "c")
val funVararg2 = funVararg(strings = *arrayOf("a", "b", "c"))
16.表达式
val singleExpression1 = 1 * 2
val singleExpression2 = { println("hello") }
17.when()
fun testWhen(any: Any) = when (any) {
is String -> any.length
is List<*> -> any.size
else -> 0
}
18.扩展函数
定义在类外的任何地方,定义
类.方法名
扩展函数不能访问私有的或者受保护
的成员
使得你可以像调用成员方法一般,调用扩展函数
扩展函数,让 JVM 上的静态语言,也能拥有像动态语言一般,扩展语言特性的能力。
爱用扩展函数
而不是创建工具类Utils
fun String.concat(str:String)="$this$str"
println("1".concat("2"))//12
fun T?.println() = println(this)
"1".println()
1.println()
19.作用域函数
下面只做with函数的源码分析,其他同理可证
with
//with源码
//接收俩个参数,1是泛型T 2是泛型T的一个扩展函数(无入参,返回值为泛型R)
//with调用第一个参数T的block方法,block也就是with的第二个参数,并把block的返回值作为自己的函数返回值
public inline fun with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
//示例
val b1: Boolean = with("123") {//lambda(省略了参数声明和箭头,并且lambda作为with的第二个实参可以拿到括号外声明)
//lambda作为泛型T的扩展函数,所以其作用域在T(String类)内部,也就是this即为"123"
println(this)
this.toBoolean()//可以省略this.调用,其返回值是lambda的返回值,也是with的返回值
}
run
//源码
public inline fun run(block: () -> R): R {
return block()
}
//示例
val b2: Boolean = run {
println("")
"123"
}.toBoolean()
//实用性场景分析 : 简化部分相同操作,如下两个变量都调用了show方法
run {
if (firstTimeView) introView else normalView
}.show()
T.run
val b3: Boolean = "123".run {
println(this)
toBoolean()//省略this.
}
T.let
val b4: Boolean = "123".let { a ->
//a替换it 也可省略
println(a)
a.toBoolean()
}
//操作符配合使用
nullVal?.let {
println("[nullVal] not null code block")
} ?: run {
println("[nullVal] null code block")
}
20.Kotlin DSL
//示例1
fun kotlinDSL(block: StringBuilder.() -> Unit) {
block(StringBuilder("Kotlin"))
}
fun testDSL1() {
// 调用高阶函数
kotlinDSL {
// 这个 lambda 的接收者类型为StringBuilder
append(" DSL")
println(this)
}
}
//示例2
class Model {
var id: String = ""
var name: String = ""
var subItem: SubItem? = null
class SubItem {
var id: String = ""
var name: String = ""
}
}
inline fun Model(config: Model.() -> Unit) : Model {
val result = Model()
config(result)
return result
}
//调用
Model {
id = "123"
name = "Name"
}
21.自定义操作符
operator fun String.rem(other: String?): String {
return this + (other ?: "")
}
operator inline fun Int.rem(blk: () -> Unit) {
if (Random(System.currentTimeMillis()).nextInt(100) < this) blk()
}
fun testOperator2() {
val result = run { "" % "" }
50 % { "你有一半的几率看到我!".println() }
}
22.中缀调用
简化了 "a.(b)" 转化成中缀 "a 中缀 b"
val pair :Pair = "1" to 1
val map1 = mapOf("1" to 1, "2" to 2)
自定义中缀
object 烟雨
object 天青色 {
infix fun 等(word: 烟雨) {
println("天青色等烟雨")
}
}
infix fun Int.除(i: Int): String = ""
fun main3(args: Array) {
天青色 等 烟雨
9 除 3 === "除了你还是你~"
Love You 3000
}
object Love {
infix fun You(i: Int) {
println("I Love U 3000")
}
}
23.invoke
Kotlin 提供了 invoke 约定,可以让对象向函数一样直接调用
class Person(val name: String) {
operator fun invoke() {
println("my name is $name")
}
}
fun testInvoke1() {
val person = Person("geniusmart")
person()
}
24.实现Gradle依赖声明
class Dependencies {
fun compile(coordinate: String) {
println("add $coordinate")
}
operator fun invoke(block: Dependencies.() -> Unit) {
block()
}
}
fun testInvoke2() {
val dependencies = Dependencies()
dependencies {
compile("com.android.support:appcompat-v7:27.0.1")
compile("com.android.support.constraint:constraint-layout:1.0.2")
}
// 等价于:
dependencies.compile("com.android.support:appcompat-v7:27.0.1")
dependencies.compile("com.android.support.constraint:constraint-layout:1.0.2")
}
BONUS TIME
http://fuckjava.com