Kotlin的一些特性记录

Posted 做一个苦行僧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin的一些特性记录相关的知识,希望对你有一定的参考价值。

Kotlin的解构

在Kotlin中可以把一个对象赋值给多个变量,这种操作叫做解构说明

data class Boy(var age:Int,var name:String)

var boy = Boy(35,"周杰伦")
//下面这两种方式 都可以获得变了的值
var(age,name) = boy
var(age1,name1) = boy

上面用关键字data修饰了class ,被称之为数据类,数据类之所以可以使用解构声明,是由于数据类比较特殊,我们可以在app/bulid/tmp/kotlin-calsses目录下,查看生成之后的代码java代码

public final data class Boy public constructor(age: kotlin.Int, name: kotlin.String) 
    public final var age: kotlin.Int /* compiled code */

    public final var name: kotlin.String /* compiled code */

    public final operator fun component1(): kotlin.Int  /* compiled code */ 

    public final operator fun component2(): kotlin.String  /* compiled code */ 

而我们可以通过以下方式查看上面的data class 生成的字节码

 

 其实就是 componet1方法 将age返回 ,component2将name返回,我们在通过上面Kotlin Bytecode区域的左上角DECOMPILE 可以查看kotlin翻译成的java代码实现

上面的componentN()函数就是解构的核心

自定义解构

对于一个普通的类不具备解构的功能,除非像我们上面使用data来声明。但是一个自定义的普通的class我们还是可以手动声明operator fun componentN() 方法的方式来实现解构的功能,我们将上面的Boy类的data修饰符去掉

class Boy(var age:Int,var name:String)
    operator fun component1() = age
    operator fun component2() = name

这样我们同样能够使用解构功能。像原来我们需要返回一个具体的Boy类型 然后通过boy.age的多个参数值的方式我们可以用下面的方法改写 

fun getBoyInfo(): Boy  return Book(42, "Kobe")  
val (age, name) = getBoyInfo() //这样也精简了代码

infix关键字

infix关键字修饰的函数,在Kotlin中 就是允许 函数采用中缀表达式的形式去调用,中缀表达式的使用需要满足3个前提条件

  1. 必须是成员函数或者扩展函数
  2. 函数只有一个参数
  3. 参数不可能是可变参数或者默认参数

像我们平时使用Kotlin中的map的时候 对map进行初始化的时候 可以这样

var map = mutableMapOf<String,String>("age" to "12","name" to "周杰伦")

其实 这里的 to 就是使用了 infix修饰了的一个扩展函数

public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

其实我们也可以这样调用

但是androidStudio会提示我让我 使用 ‘to’ infix的形式,其实这种方法也没有什么特别之处,我觉得唯一的特别之处 就是我们的 代码更接近于人类的自然 语言,我们用下面的例子更加能够说明

class Person infix fun sayHelloTo(name : String) println("你好 $name" )  

我们没有加infix修饰 就可能是下面的代码 

var kobe = Person()
kobe.sayHelloTo("周杰伦")

必须使用方法的调用,但是使用infix之后,我们可以用类似 中文语言的形式使用代码

kobe sayHelloTo "周杰伦"

是不是感觉很奇妙

扩展函数和扩展属性

kotlin有一个特别好使用的的功能叫做扩展,你可以给已有的类去额外添加函数和属性,而且既不需要修改源码也不需要写子类,比如我们可以给Float 扩展一个dp属性

val Float.dp
  get() = TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP,
    this,
    Resources.getSystem().displayMetrics
  )

...
//这里就可以直接使用了,
val RADIUS = 200f.dp

这里我们就可以直接使用200f.dp 了 ,不需要我们平时使用写一个单独的utils类,写一个静态方法,然后每次调用的时候 都调用这个utils的静态方法,这样可以省略掉不少代码

同样的我们也可以给Float定义 dp函数,我们将上面的扩展属性修改为 扩展函数,

fun Float.dp() = TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP,
    this,
    Resources.getSystem().displayMetrics
  )

...
//这里就可以直接使用了,
val RADIUS = 200f.dp()

这样我们可以使用扩展函数的形式调用了,虽然这个dp的例子有些不恰当,但是我们也可以看到扩展函数或者扩展属性的魅力。

我们可以将我们的扩展函数 或者扩展属性直接写在kotlin文件中,然后在同module下面,我们所有的类种都可以访问到扩展函数或者扩展属性,例如下面,新建一个TimeUtils.kt的文件,不需要在这里文件中声明class文件,直接在里面编写kotlin扩展函数就行

 

这就是kotlin的Top-Level ,我们可以在其他任何地方使用这些扩展方法

Kotlin的高阶函数

在Kotlin中,我们将参数有函数类型或者返回值是函数类型的函数,都叫做高阶函数。例如下面的函数

fun sum(num:Int,a:(Int)->Int,b:(Int)->Int):Int
    return a(num)+b(num)

我们可以将sum函数看作高阶函数

调用的时候 我们可以按照下面的方式调用

var result = sum(3,param1-> param1 +3 ,param2->param2+4)

这里将上面的 函数类型的参数 写成了两个lambda的形式,我们也可以在函数sum函数中写匿名函数

sum(base, fun(i:Int):Int  return i+3 ,fun (i:Int):Int  return i+4 )

这个其实与上面lambda的一种表现形式

还有另外一种写法 ,将函数作为一个对象来使用

fun fun1(i:Int):Int  return i+3 

fun fun2(i:Int):Int  return i+4 

fun sum(num:Int,a:(Int)->Int,b:(Int)->Int):Int
    return a(num)+b(num)



var base = 3
var result = sum(base, ::fun1,::fun2)

这样使用和上面得到同样的效果

在官方的文档中 将 :: method 这种写法叫做函数引用 Function Reference 这里是官方的说法。但是更喜欢“扔物线”文章中的说法 ,函数加上两个冒号,就可以将函数变成一个对象。Kotlin 里「函数可以作为参数」这件事的本质,是函数在 Kotlin 里可以作为对象存在——因为只有对象才能被作为参数传递啊

有了函数对象这样一个概念,我们就可以将函数赋给一个变量

var base = 3
var c = ::fun1
var result = sum(base, c,::fun2)

当然我们可以将一个函数的定义 赋值给一个变量,然后将变量直接作为函数参数调用

var function1 = fun(i:Int):Int  return i+3 

fun fun2(i:Int):Int  return i+4 

var base = 3
var result = sum(base, function1,::fun2)

// 我们也可以这么调用
function1(2)

// fun2的另外一种调用方法
(::fun2)(2)  //实际上调用的是 (::fun2).invoke(2)

Lambda 表达式

如果 Lambda 是函数的最后一个参数,你可以把 Lambda 写在括号的外面:

view.setOnClickListener()  v: View ->
   //doSomething

而如果 Lambda 是函数唯一的参数,你还可以直接把括号去了:

view.setOnClickListener  v: View ->
   //doSomething

另外,如果这个 Lambda 是单参数的,它的这个参数也省略掉不写:

view.setOnClickListener 
   //doSomething

哎呦,不错额,单参数的时候只要不用这个参数就可以直接不写了。其实就算用,也可以不写,因为 Kotlin 的 Lambda 对于省略的唯一参数有默认的名字:it:

view.setOnClickListener 
  // doSomething
  it.setVisibility(GONE)

以上是关于Kotlin的一些特性记录的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin的一些特性记录

快速上手 Kotlin 开发系列之反引号

Kotlin 1.5新特性记录

Kotlin 1.5新特性记录

Kotlin 1.5新特性记录

Kotlin的构造方法探究