这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战 | 创作学习持续成长,夺宝闯关赢大奖

Lambda表达式

Lambda固然好用 ,但是你知道Kotlin是如何实现的吗 ?

kotlin代码

fun foo(item: Int) = { print(item) }

多线程和多进程的区别换为 java字节码

@NotNull
public final Function0 foo(final int item) {
  return (Function0)(new Function0() {
   // $FF: synthetic method
   // $FF: bridge method
   public Object invoke() {
     this.invoke();
     return Unit.INSTANCE;
    }

   public final void invoke() {
     int var1 = item;
     System.out.print(var1);
    }
  });
}

可以看到这个 Lambda方法体 实际上是Function0的一个匿名内部类 。

Function没有参数所以是0,如果有安全一个参数就是Function1 ,Java 最多到22变量是什么意思, 但是Kotlin设计了FunctionN

综上所述 ,多处使用Lambda表达式会使程序中的匿名内部类增多 ,这里Kotlin团队肯定也想到了 ,所以他们提供了inline函数来解决这个问题 。

lazy

lazy的背后是接受一个lambda变量的指针其含义是指该变量的返回一个Lazy<T>实例的函数,第一次访问该属性时,会执行lazy对应的Lambda表达式并记录结果,后续访问该属变量是什么意思性时只是返回记录的结果。

private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    //lambda 表达式,负责拿对象示例
    private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    // final field is required to enable safe publication of constructed instance
    private val lock = lock ?: this
    override val value: T
        get() {
            val _v1 = _value
            if (_v1 !== UNINITIALIZED_VALUE) {
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }
            return synchronized(lock) {
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    //拿到对象之后进行赋值给_value
                    val typedValue = initializer!!()
                    _value = typedValue
                    initializer = null
                    typedValue
                }
            }
        }
    override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
    ...
}

Lazy的同步锁:系统会给lazy属性默认加上同步锁,也就是LazyThreadSafetyMode.SYNCHRONIZEDjson文件是干什么的它在同一时刻只允许一个线程对lazy属性进行初始化,所以它是线程安全的。 (当然也可以传递给Lajson文件是干什么的zy LazyThrea安全教育平台登录入口dSa多线程fetyMode.NONE这个属性,表示不会有多线程方面的处理,也就没kotlin下载有线程方面的开销)

还有一个lateinit也可以实现延迟赋值 ,但是这个属性不支持基本数据类型的参数 。

[如果基础类型的数据也想要延时加载怎么办 ?] 通过Delegates.notNull<T>可以实现

val index by Delegates.notNull<Int>()

有两种方式变量名可以实现延时初始化: by lazy 和 lateinit

/**
 * 懒加载
 * 变量必须是val修饰
 * 首次调用进行赋值,赋值之后将不可再修改
 *
 * 系统会默认给lazy 属性默认加上同步锁 ,同一时刻只有一个线程访问 ,保证安全 ,当然也可以设置为NONE来关闭提升性能
 *
 */
val sex by lazy(LazyThreadSafetyMode.NONE) {
  if(color) "male" else "female"
}

//与lazy不同 lateinit可以延时赋值 但是它不能用于基础类型
lateinit var mName: String

//这样会报错 ,报错信息 : 'lateinit' modifier is not allowed on properties of primitive types
//如果想要不报错有两种方式: 
//1. 用Integer类型替代
//2. 使用委托 Delegates.notNull<T> 的方式
lateinit var mAge: Int

fun setName(name: String) {
  this.mName = name
}

数据类

kotlin

data class CarInfo(val name: String, val color: String)

转换成Java

public final class CarInfo {
   @NotNull
   private final String name;
   @NotNull
   private final String color;
   @NotNull
   public final String getName() {
      return this.name;
   }
   @NotNull
   public final String getColor() {
      return this.color;
   }
   public CarInfo(@NotNull String name, @NotNull String color) {
      Intrinsics.checkNotNullParameter(name, "name");
      Intrinsics.checkNotNullParameter(color, "color");
      super();
      this.name = name;
      this.color = color;
   }
   @NotNull
   public final String component1() {
      return this.name;
   }
   @NotNull
   public final String component2() {
      return this.color;
   }
   @NotNull
   public final CarInfo copy(@NotNull String name, @NotNull String color) {
      Intrinsics.checkNotNullParameter(name, "name");
      Intrinsics.checkNotNullParameter(color, "color");
      return new CarInfo(name, color);
   }
   // $FF: synthetic method
   public static CarInfo copy$default(CarInfo var0, String var1, String var2, int var3, Object var4) {
      if ((var3 & 1) != 0) {
         var1 = var0.name;
      }
      if ((var3 & 2) != 0) {
         var2 = var0.color;
      }
      return var0.copy(var1, var2);
   }
   @NotNull
   public String toString() {
      return "CarInfo(name=" + this.name + ",hljs-keyword">this.color + ")";
   }
   public int hashCode() {
      String var10000 = this.name;
      int var1 = (var10000 != null ? var10000.hashCode() : 0) * 31;
      String var10001 = this.color;
      return var1 + (var10001 != null ? var10001.hashCode() : 0);
   }
   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof CarInfo) {
            CarInfo var2 = (CarInfo)var1;
            if (Intrinsics.areEqual(this.name, var2.name) && Intrinsics.areEqual(this.color, var2.color)) {
               return true;
            }
         }
         return false;
      } else {
         return true;
      }
   }
}

, 有点恐怖,但是也不要慌,大部分都是get set方法 ,不过有两个方法是Java原来没有的,componentcopy,接kotlin为什么流行不起来下来我们就来看一看这两个方法。

copy

public static CarInfo copy$default(CarInfo var0, String var1, String var2, int var3, Object var4) {
    if ((var3 & 1) != 0) {
        var1 = var0.name;
    }
    if ((var3 & 2) != 0) {
        var2 = var0.color;
    }
    return var0.copy(var1, var2);
}

可以看到新生成的对象 var0的值将直接复制原地址的值 。安全中心

这好像是浅拷贝呀,我们来做了实验 。变量的定义

val carInfo = CarInfo("哈弗大狗", "黑色", "benzine")
//这里用到了解构
val (name, color, engine) = carInfo
val carInfo1 = carInfo.copy()
//* !!!这里注意 基础数据类型不包括String ,但是常量赋值的String,对象地址本来就改变了,所以不会出问题
carInfo1.engine = "xxxxxx"
println(carInfo1.engine)
println("$name, ${color},${carInfo.engine}")
>>>>>>>>>
xxxxxx
哈弗大狗, 黑色,benzine

咦~ 好像没什么问题呀 ! 复制后的对象修改属性也没有对元对象进行修改 。

这是因为虽然基安全础数据类型不包括String ,但是常量赋值的String, 对象地址本来就改变了,所以不会出问题

浅拷贝需要注意的地方是:除了基础类型的数据 ,其他的类型都是复制引用 ,也就是引用的同一个对象 。

这次我们试一试增加一个类看一看。

比如咱们变量名carInfo中增加json解析了一个属性 Person,这是json数据一个类 ,通过copy进行浅复安全教育平台登录制后就会出安全教育平台登录入口现问题 。

//我们这样修改CarInfo类
data class CarInfo(val name: String, var color: String) {
    operator fun component3(): Person {
        return person
    }
    var person = Person("json")
    //次构造函数
    constructor(name: String, color: String, person: Person): this(name, color) {
        this.person = person
    }
}
data class Person(var name: String)
val carInfo = CarInfo("哈弗大狗", "黑色", Person("benzine"))
val (name, color, person) = carInfo
//注意我们这里还是浅拷贝
val carInfo1 = carInfo.copy()
carInfo1.person.name = "xxxxx"
carInfo1.color = "红色"
println("carInfo1 ${carInfo1.person.hashCode()}  carInfo ${carInfo.person.hashCode()}")
println("$name, ${carInfo.color},${carInfo.person.name}")
>>>>>>>>>>>>>>>>
carInfo1 114516600  carInfo -222081999
哈弗大狗, 黑色,benzine

意想不到的事情又发生了 ,不对不对 ,一定是哪里出问题变量4了(我的推论怎么可能出问题呢) 。
答案在CarInfo中 ,因为我们的person在次构造方法中 ,所以自动生成的copy方法也没有处理这个person ,也就是没有安全模式怎么解除进行对象赋值 ,所以直接使用的是创建的那个原始person ,名叫json

operator fun component3(): Person {
    return person
}
//这里重新创建了一个对象
var person = Person("json")

这里也提醒我安全教育平台登录们在data class中尽量不要使用次构造函数 ,会带来种种问题 。

细心的同学注意到了我们加入安全person的时候,多加入了一个Component方法。这个方法是干啥的 ?

这里与一个概念解构有关 ,为了可以顺利的实现 解构,所以加入了这个方法 ,Kotlin默认对主构造函安全中心数的属性自动生成component方法 ,而次构造函数就没有 ,所以需要手动生成 。

如果将次构造删掉 ,将person移动到主构造中,就会发现浅拷贝的问题出来了

>>>>>>>>>>>>>>>>

carInfo1 114516600 carInfo 114516600
//copy对象的更改导致了 原来对象值的变化
哈弗大狗, 黑色, xxxxx

嗯,Nice 。变量的定义

问题揪出来了,多线程渲染我们该如何解决呢 ?

这里只需要实现一个深拷贝就可以了。

fun deepCopy(): CarInfo {
    val carInfo = this.copy()
    //其实也很简单就是调用了子类对象的copy方法
    carInfo.person = carInfo.person.copy()
    return carInfo
}

其实浅拷贝和深拷贝问题不是重点 ,这里主要是为了分析Kotlin版本该如何实现,如何避免错误。

内联函数

fun main() {
    foo {
        println("在方法块里 输出一个东西")
    }
}
fun foo(block:() -> Unit) {
    block.invoke()
    println("输出一个东西")
}
>>> 转换成java
public final void main() {
    //传入一个Function0对象 这里就是很厉害了 每次都是创建一个对象
    this.foo((Function0)null.INSTANCE);
}
public final void foo(@NotNull Function0 block) {
    Intrinsics.checkNotNullParameter(block, "block");
    block.invoke();
    String var2 = "输出一个东西";
    System.out.println(var2);
}
//加入内联之后呢
inline fun foo(block:() -> Unit) {
    block.invoke()
    println("输出一个东西")
}
>>> java
public final void main() {
    int $i$f$foo = false;
    int var3 = false;
    //可以看到执行的代码都被内嵌在这里了
    String var4 = "在方法块里 输出一个东西";
    System.out.println(var4);
    String var5 = "输出一个东西";
    System.out.println(var5);
}
public final void foo(@NotNull Function0 block) {
    int $i$f$foo = 0;
    Intrinsics.checkNotNullParameter(block, "block");
    block.invoke();
    String var3 = "输出一个东西";
    System.out.println(var3);
}

以上就是内联函数的优化

接下来我们来看个示例

fun main() {
    foo {
        return
    }
}
inline fun foo(block:() -> Unit) {
    println("输出一个东西")
    block.invoke()
    println("输出两个东西")
}

以上情况会输出什么呢 ?

答案是:输出一个东西

这里就是内联函数的一个小坑,他会使代码提前返回,其实看懂了内联的实现之后,这个提前返回也很容易想明白

最后

文章中如果有问题的地方,欢迎大家多多评论指正,json怎么读另外还是希望得到大家的点赞支持✨