kotlin学习笔记-进阶内容

本文深入探讨Kotlin中的Lambda表达式简化、infix与inline关键字的使用、data类特性、函数嵌套技巧、扩展函数与属性的应用、抽象属性定义及标准函数介绍。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Lamada表达式简化

当一个函数传入的参数为lamada并且这个参数是最后一个,那可以将lamada表达式放在括号外面。

viewList<View>.forEach({view:View
    print(view.toString)
})

由于kotlin可以进行类型判断,所以可以简化为:

viewList<View>.forEach({view->
    print(view.toString)
})

如果lamada函数有且仅有一个参数,可以继续简化为:

viewList<View>.forEach({
    print(it.toString)
})

将lamada表达式移到括号外面,括号省略,最终为:

viewList<View>.forEach{
    print(it.toString)
}
infix 关键字

当一个函数逻辑比较简单且只有一个参数,该函数可以用infix关键字修饰:

class A{
   	infix fun add(a:Int):Int{
        return 2+a
   	}
}

var a = A()
var b = a add 3
print(b)  //b=5
inline关键字
  • 用inline关键字修饰的函数就是内联函数。它的工作原理就是把它的内联函数体复制到它的调用处。它的意义就是减少一层调用栈。
inline fun log(text:String){
    Log.e("TAG",text)
}

fun main(){
    log("Hello")
}

通过字节码文件可以看出,添加了inline关键字后,main函数里面调用的是

Log.e("TAG","Hello")

而如果不用inline关键字修饰,main函数里面调用的是

log("Hello")

也就是说前者的栈中只有两个函数(main() 、e() ),而后者的栈中有三个函数(main()、log()、e() )。

  • 内联函数会增加编译器的工作量, 内联函数在配合函数传入参数类型是函数类型的时候效果最好(函数类型编译后会额外创建一个对象)
inline fun measureTime(action:()->Unit){
    var beforeTime = System.currentTimeMillis()
    
    action()
    
    var afterTime = System.currentTimeMillis()
    
    println("the total time is ${afterTime - beforeTime}")
}

fun main(){
    measure{
        println("Hello")
    }
}

通过反编译上面代码的字节码文件可以看出,如果使用inline关键字修饰,main函数执行measureTime函数会将measureTime函数里面的代码复制插入过来

public static final void main() {
      int $i$f$measureTimes = false;
      long beforeTime$iv = System.currentTimeMillis();
      int var3 = false;
      String var4 = "Hello";
      boolean var5 = false;
      System.out.println(var4);
      long afterTime$iv = System.currentTimeMillis();
      String var9 = "the total time is " + (afterTime$iv - beforeTime$iv);
      boolean var8 = false;
      System.out.println(var9);
   }

如果不使用inline关键字修饰,不仅会增加一个调用栈,而且会创建一个传入函数参数的对象(Function0,因为java的函数是无法作为参数传递的,所以会将函数包装成一个类来进行传递),如果是一个多次重复的操作,就会创建大量对象

  public static final void measureTimes(@NotNull Function0 action) {
      Intrinsics.checkParameterIsNotNull(action, "action");
      long beforeTime = System.currentTimeMillis();
      action.invoke();
      long afterTime = System.currentTimeMillis();
      String var5 = "the total time is " + (afterTime - beforeTime);
      boolean var6 = false;
      System.out.println(var5);
   }
data类
data class Student(var name:String , var age:Int) {
	
}

kotlin会自动生成get&set方法,相比较Java,kotlin的data类还会生成一个copy函数,将字节码文件反编译可以看到:

public class Student {
	 ......
    
   public final Student copy(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      return new Student(name, age);
   }
}

var student = Student()
var studentCopy = student.copy()
print(student == studentCopy)	//true 两个等号调用equals函数,而copy重写了equals函数
print(student === studentCopy)	//false	三个等号比较的是地址值
解构

在java中一次调用只能返回一个值,如果需要多个值,就得将它们包装成一个对象,在使用的时候再从这个类中取出多个值来,依次进行赋值

data class Response(var code:Int, var message:String, var student:Student){
    fun excute():Response{
        val code = 200
        val message = "请求成功"
        val student = Student("张三",20)
        return Response(code,message,student)
    }
}

fun main(){
  val response = excute()
  val code = response.code
  val message = response.message
  val student = response.student  
}

将data类的字节码文件进行反编译,会发现有和成员变量相对应的component函数

public class Response {
    
    ......
    
	 @NotNull
   public final int component1() {
      return this.code;
   }

   public final String component2() {
      return this.message;
   }
     public final Student component3() {
      return this.student;
   }
}

通过解构的方法,可以将代码优化为:

fun main(){
    val(code,message,student) = excute()
    // code = excute().component1()
    // message = excute().component2()
    // student = excute().component3()
    print(code)
    print(message)
    print(student)
}

实际上就是通过对应的component方法进行赋值,与构造相反

函数嵌套

当一个函数只在一个地方用到并且也不希望在别的地方调用,可以进行函数的嵌套:

fun A(){
    var a = 1
    fun B(){
        print(a)
    }
}

内部函数可以访问外部函数的参数,每次调用外部函数都会生成内部函数的对象,因此外部函数在频繁调用的时候不要使用嵌套函数

注解
class BaseApplication : Application(){
   
    override fun attachBaseContext(context:Context?){
        super.attachBaseContext(context)
        this.currentApplication = context
    }
    
    companion object{
        @get:JvmName(currentApplication)
        @JvmStatic
        lateinit var currentApplication:Context
       		private set
        
        fun init(){
            ......
        }
    }
    
}
  • 由于kotlin会自动帮我们生成var变量的get和set方法,如果我们不希望set方法被调用,可以通过private set来私有化set方法。
  • 在kotlin中没有static关键字,因此用伴生类companion object来代替static进行使用,在kotlin类中,可以通过BaseApplication.currentApplication或者BaseApplication.init()来进行调用,而在java中,只能通过BaseApplication.Componion.init()来调用函数,而无法对currentApplication变量进行调用,若想进行调用,需要添加@JvmStatic注解
  • @get:JvmName()注解是修改调用get方法的函数名,注解后可以直接在java中调用BaseApplication.currentApplication而不需要调用BaseApplication.getCurrentApplication(),@get表示生效的范围是get方法,如果是@file表示生效的范围是当前文件
//文件名为Display.kt
@file:JvmName("DisPlayUtils")

fun dp2px(){
    ......
}

注解前在外部文件调用dp2px方法:DisplayKt.dp2px(), 注解后在外部的调用方法为:DisplayUtils.dp2px()

函数参数默认值
object Utils{
    @JvmStatic
    fun toast(message:String?){
        toast(message,Toast.LENGTH_SHORT)
    }
    
    @JvmStatic
    fun toast(message:String?, duration:Int){
        Toast.makeText(mContext, message?:"", Toast.LENGTH_SHORT).show();
    }
}

通过上面可以看到,java中的函数重载存在着很多重复的地方,而为了代码简洁,在kotlin中可以使用函数参数默认值

object Utils{
    @JvmStatic
    fun toast(message:String?){
        toast(message,Toast.LENGTH_SHORT)
    }
    
    @JvmStatic
    fun toast(message:String?, duration:Int){
        Toast.makeText(mContext, message?:"", Toast.LENGTH_SHORT).show();
    }
}

通过上面可以看到,java中的函数重载存在着很多重复的地方,而为了代码简洁,在kotlin中可以使用函数参数默认值

object Utils{
 	@JvmOverloads
    @JvmStatic
    fun toast(message:String?, duration:Int = Toast.LENGTH_SHORT){
        Toast.makeText(mContext, message?:"", Toast.LENGTH_SHORT).show();
    }
}

Utlils.toast("success")  //不加@JvmOverloads在kotlin中不会报错而在java中调用会报错

@JvmOverloads注解会针对默认参数生成重载方法,如果不加该注解在kotlin中没问题,但是再java中只传一个参数会报错

扩展函数
  • 假设有一个将dp转px的方法
fun dp2px(value:Float):Float{

    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,value,Resources.getSystem().displayMetrics)

}

那我们在调用这个方法的时候就是

view.width = Utils.dp2px(10f)

可以看出这种调用方法不是很方便代码阅读,而在我们没有办法更改源码的前提下,就需要用到kotlin提供的扩展函数,将上述工具类的方法进行改造后如下:

fun Float.dp2px():Float{

    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,this,Resources.getSystem().displayMetrics)

}

即在扩展函数名字前加上需要添加扩展函数的类,用“.”进行分隔,改造后的扩展函数的调用就是:

view.width = 10f.dp2px()

需要注意的是,若扩展函数和成员函数一模一样,那么总是会执行成员函数。

扩展属性

同扩展函数类似,我们也可以在不改变源码的前提下给一个类添加成员属性

val ViewGroup.firstChild:View
	get() = getChildAt(0)
抽象属性
  • 在java中只有抽象方法,在kotlin中可以创建抽象属性
interface BaseView<T>{
	val presenter:T
}
by关键字
  • 代理委托
    假如现在有一个需求,有一个token属性,每次改变的时候都要缓存到本地,取的时候也先从本地取,那我们可以这样写:
var token:String
	set(value){
		CacheUtils.save("token",value)
	}
	get(){
		return CacheUtils.get("token")
	}

但是如果有好多的token的话,就会非常麻烦,这个时候我们就可以用到委托代理(未完待续…)

var token1:String
set(value){
		CacheUtils.save("token",value)
	}
	get(){
		return CacheUtils.get("token")
	}
var token2:String
	......

var token3:String
	......
标准函数
  • 又叫作用域函数,他的作用是将一些逻辑连贯的代码放到同一个组用于当中的,这样可以增加代码的可读性。
  • apply 特别适合对一个对象初始化的时候做附加操作。
  • let 配合空判断的时候比较合适
  • also
  • run

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值