侧边栏壁纸
博主头像
Z同学博主等级

工作磨平激情前,坚持技术的热忱。 欢迎光临Z同学的技术小站。 分享最新的互联网知识。

  • 累计撰写 274 篇文章
  • 累计创建 55 个标签
  • 累计收到 74 条评论

Kotlin学习-函数介绍,SAM,typealias,infix,varargs

Z同学
2021-07-19 / 0 评论 / 0 点赞 / 415 阅读 / 5,573 字
温馨提示:
本文最后更新于 2021-12-03,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

继续深入学习,并记录相关的知识点。

高阶函数

高阶函数是指将函数作为参数或者返回值的函数。

介绍:Kotlin使用类似 (Int) -> String 的一系列函数类型来处理高阶函数的声明。例如: val onclick:() ->Unit = ....

这些函数有下面几种特点

  • 所有函数类型都一个圆括号括起来的参数类型列表,以及一个返回类型:例如(A,B) -> C 表示两个接受类型,分别为AB,并返回一个C类型的值。
  • 传参可以为空: 例如()-> C表示没有接受类型,但是有一个返回类型为C的值。
  • 返回类型为空时不能省略:普通函数返回值为空的时候,我们可以省略Unit 关键字,如果是高阶函数则不行。实例 :(A,B)->Unit 表示两个类型传参,返回值为空。
  • 函数类型可以有一个额外的接收者类型,需要在表达式的点号之前指定。实例:A.(B) ->C 表示可以在A的接收者对象上以一个B类型参数来调用并返回一个C类型的值。
  • 挂起函数属于特殊种类的函数类型,它的表示法中有一个suspend修饰符,例如 supspend() -> Unit 或者 suspend A.(B) -> C

函数类型标识符可以选择性的包含函数的参数名:(x :Int ,y:Int)-> Point

如果需要函数类型指定为可以为空: ((Int,Int)-> Int)? 需要用括号包裹起来。

那么,问题来了。高阶函数是如何实例化?

1.使用函数字面值的代码块实现:

  • Lambda表达式 :{ a, b -> a + b }
  • 匿名函数 :fun(s: String): Int { return s.toIntOrNull() ?: 0 }

2.使用已有声明的可调用引用:

  • 顶层,局部,成员,扩展函数: ::isOdd, String::toInt
  • 顶层,成员,扩展属性:List<Int> :: size
  • 构造函数: ::Regex
  • 指向特定实例成员的绑定的可调用引用:foo::toString

3.使用实现函数类型接口的自定义类的实例:

class IntTransformer: (Int) -> Int {
    override operator fun invoke(x: Int): Int = TODO()
}

val intFunction: (Int) -> Int = IntTransformer()

函数式(SAM)接口

介绍: 在Kotlin之中,只有一个抽象方法的接口被称为函数式接口,或者SAM(单一抽象方法)接口。SAM的全称:Single abstract method

函数式接口可以有多个非抽象的成员(函数,属性)。但是只能有一个抽象成员。

只有函数式接口 可以在interface前面加上fun修饰符

实例1:

fun interface KK {
    fun invoke() 
}

因为Kotlin针对SAM接口提供了一种简洁的转换。

通常情况下,我们实现接口后,需要将它的抽象方法实例化。

实例2:

fun interface KK {
    fun invoke(i: Int): Boolean
}

fun main(array: Array<String>) {
    val ee = object : KK {
        override fun invoke(i: Int): Boolean {
            return i % 2 == 0
        }
    }
}

但是针对只有一个抽象方法的接口。我们可以将上面的方法直接简写为

实例3:

fun interface KK {
    fun invoke(i: Int): Boolean
}

fun main(array: Array<String>) {
     val ee = KK { it % 2 == 0 }
}

这两个在程序上来说,结果是等价的。

it 是一个关键字,可以用来代替只有一个传参的参数。

如果接口传的两个参数

实例4:

fun interface KK {
    fun invoke(i: Int, i2: Int): Boolean
}

fun main(array: Array<String>) {
    val ee = KK { i, i2 -> i > i2 }
}

类型别名(关键字:typealias)

介绍:类型别名为现有类型提供替代名称。如果类型名称太长,我们可以引入较短的名称,并使用新的名称,替代原类名称。

类型别名不会创建新的类型。他们只是给现有类型取了另一个名称而已

它有助于缩短较长的泛型类型名称。可以让我们更直观的了解函数想表达的意义

实例1:

interface Test {
    
    fun test1(restaurant: Array<String>): Array<String> {
        return arrayOf("Z同学", "Y同学")
    }

针对这种情况 我们可以将Array<String> 进行别名定义

typealias  UserList = Array<String>

interface Test {
	//第一种 普通模式
    fun test1(restaurant: Array<String>): Array<String> {
        return arrayOf("Z同学", "Y同学")
    }
	//第二种 使用了别名
    fun test2(restaurant: UserList): UserList {
        return arrayOf("Z同学", "Y同学")
    }
}

我们除了可以在参数类型进行别名定义,我们还可以针对Lambda表达式定义别名。

实例2:

//定义一个不参数从,并返回String 的的函数,它的别名为aliasdSuppliser
typealias  aliasedSupplier = () -> String

fun writeAliased(supper: aliasedSupplier) {
    println(supper.invoke())
}

fun main(array: Array<String>) {
    writeAliased { "Z同学" }
}
//输出
Z同学

关于别名可以使用的场景:

// Classes and Interfaces (类和接口)
typealias RegularExpression = String
typealias IntentData = Parcelable

// Nullable types (可空类型)
typealias MaybeString = String?

// Generics with Type Parameters (类型参数泛型)
typealias MultivaluedMap<K, V> = HashMap<K, List<V>>
typealias Lookup<T> = HashMap<T, T>

// Generics with Concrete Type Arguments (混合类型参数泛型)
typealias Users = ArrayList<User>

// Type Projections (类型投影)
typealias Strings = Array<out String>
typealias OutArray<T> = Array<out T>
typealias AnyIterable = Iterable<*>

// Objects (including Companion Objects) (对象,包括伴生对象)
typealias RegexUtil = Regex.Companion

// Function Types (函数类型)
typealias ClickHandler = (View) -> Unit

// Lambda with Receiver (带接收者的Lambda)
typealias IntentInitializer = Intent.() -> Unit

// Nested Classes and Interfaces (嵌套类和接口)
typealias NotificationBuilder = NotificationCompat.Builder
typealias OnPermissionResult = ActivityCompat.OnRequestPermissionsResultCallback

// Enums (枚举类)
typealias Direction = kotlin.io.FileWalkDirection
// (but you cannot alias a single enum *entry*)

// Annotation (注解)
typealias Multifile = JvmMultifileClass

其实别名就是用来代替底层类型的实现。

可以精简代码的复杂程度。

别名其实主要是用来代替数据类型的写法它的使用场景是:

  • 在声明变量类型,参数类型和返回值类型的时候。
  • 在作为类型参数约束和类型参数的时候
  • 在使用比较类型is 或者强转类型 as的时候
  • 在获得函数引用的时候

注意typealias 只能定义在kt文件中。它不能内嵌到一个类,对象,接口或者其他的代码块中。否则会提示错误:Nested and local type aliases are not supported

同时:针对typealias 也可以添加访问权限修饰符: internalprivate

Import As 引入类的别名

介绍:在Kotlin之中有一个很类型别名很类似的概念,就是import as

它主要是在我们导入了相同名称的类时, 避免第二个类名称写的时候过长问题。我们可以给这个导入的类 创建一个别名而已。

实例:

import cn.zinyan.demo.kotlin.d.Test1 as TT
import cn.zinyan.demo.kotlin.e.Test1


fun main(array: Array<String>) {
    var te1 = TT()
    var te2 = Test1()
}

例如我们在两个包中都存在Test1 对象。但是我们在当前类下,同时使用这两个Test1的时候 。我们可以通过as 给改个别名,避免冲突

中缀函数(关键字:infix)

介绍:我们在使用mapOf 的时候,使用了很多 to

val map = mapOf(1 to "one", 2 to "two", 3 to "three")

这种写法就是因为mapOf使用了中缀函数造成的。

在mapOf的源码中我们可以看到:public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that) 使用了infix关键字

infix函数必须满足以下几个条件:

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

实例:

infix fun String.builder(str: String): String {
    return StringBuilder(this).append(str).toString()
}

fun main(array: Array<String>) {
    var name = "zin".builder("yan")
    println(name)
	//也可以这样表示 两个方法等效
    name = "zin" builder "yang"
}

输出:

zinyan

注意:

中缀函数调用的优先级低于算数操作符,类型转换以及rangeTo操作符

而高于布尔操作符:&& 与 || , is- 与in- 检测以及其他一些操作符。

那么中缀函数有什么用呢?

它就是用来省略调用函数时候的 :".() 的"

Kotlin之中常见的使用中缀函数的还有for (i in 0 until 10 step 2 ) { .. }

他的完整使用应该是: 0.until(10).step(2) --中缀之后--> 0 until 10 step 2

可变数量的参数(关键字:varargs)

介绍:在java之中,我们如果传参数量是可变数组的话我们通常这样定义

public void str(String... str){
    
}

使用... 来代替。而在Kotlin之中使用vararg关键字来修饰。

实例:

class Test {
    fun asList(vararg ss: String) {
        for (t in ss) {
            println("t :$t")
        }
    }
}

fun main(array: Array<String>) {
    var ss = Test()
    ss.asList("A", "B", "C")
}

输出:

t :A
t :B
t :C

针对可变参数,我们还有一种情况叫做伸展操作符,就是在数组前面添加*

接着上面的实例补充

class Test {
    fun asList(vararg ss: String) {
        for (t in ss) {
            println("t :$t")
        }
    }
}

fun main(array: Array<String>) {
    var ss = Test()
    var a = arrayOf("a1", "a2", "a3", "a4")
    ss.asList("A", "B", "C", *a)
}

输出:

t :A
t :B
t :C
t :a1
t :a2
t :a3
t :a4
0

评论区