精华内容
下载资源
问答
  • kotlin特性
    2018-07-05 15:57:15

    1.Kotlin 是静态类型语言并支持类型推导,允许维护正确性与性能的同时保持原代码的简洁

    2.Kotlin 支持面向对象和函数式两种编程风格,通过头等函数使更高级别的抽象成为可能,通过支持不可变值简化了测试和多线程的并发。

    3.全面支持Java框架

    4.在Android上工作得益于紧凑的运行时、对Android API特殊的编译器的支持及丰富的库

    5.他是免费和开源的,全面支持主流的IDE和构建系统

    6.Kotlin是务实的、安全的、简洁的。

    更多相关内容
  • Kotlin特性

    2021-10-13 22:08:03
    为了避免Java工程中常出现的NPE,kotlin通过在IDE的提示帮助下避免对null对象的调用,虽然可以通过NonNull注解来实现这个功能,但是Kotlin提供了语言级别的支持,声明一个非抽象的属性时候必须给它赋予一个非空值。...

    空安全

    为了避免Java工程中常出现的NPE,kotlin通过在IDE的提示帮助下避免对null对象的调用,虽然可以通过NonNull注解来实现这个功能,但是Kotlin通过编译时检查提供了语言级别的支持,声明一个非抽象的属性时候必须给它赋予一个非空值。

    但是这样做也会产生诸多不便之处,kotlin也提供了解决方案。

    lateinit

    如果第一时间无法给变量赋值,但是确定在调用其之前可以为其赋值,声明之前加上这个关键字即可。但是这样仍然不能赋予其值为null,并且必须指定类型,并且类型不能为基本类型。

    不能指定基本类型的原因是kotlin会使用null来标记这是一个延迟初始化属性,如果是基本类型,则kotlin则无法进行标记。必须指定类型的原因是,kotlin具有类型推断的特性,但不是动态语言。

    null值

    在某些情景,比如从数据库读取或者前端传回的数据确实为空,这时候需要在变量名后加?,同时,为了保证线程安全性,避免检查为非空,却被其他线程设为空的情况出现,每次调用该变量都必须在变量名后加?.

    when

    类似于Java的switch关键字,但是不同于switch 语句中的变量类型必须是: byte、short、int , char,String,kotlin的when变量类型可以是长于int 的值或者引用类型变量(对象)。

    第二点when支持类型匹配,用is关键字能根据类型来进行匹配而不是值,相当于Java的instanceof关键字,第三点when是可以有返回值的,有返回值的时候,必须要有else分支,也就是default分支,这样做是出于空安全设计考虑,为了避免没有匹配到结果返回空值。

     

    面向对象

    首先,kotlin的类默认是不能被继承的,原因和变量推荐使用val关键字一样,如果一个类不是为了被继承而设计的,他就不该被继承,以免子类更改其中逻辑,规避不可控的风险,要使类可以被继承需要在class之前加上open关键字。

    主-次构造函数

    跟在类名后面括号及其里面的内容即是主构造函数,主构造函数中的参数(可以有默认值)即是类的属性,如果要在主构造函数写一些逻辑可以卸载init函数里面,同时要想调用父类的构造函数,直接在主构造函数之后加冒号调用即可。

    次构造函数可以有多个,但是都必须直接或者间接调用主构造函数。在类体中通过constructor实现。

    数据类和单例类

    kotlin简化了POJO的写法,不需要写getter-setter,hashCode和toString方法了,同时主构函数可以跟在类名之后,因此连类体和大括号都可以省略。

    单例模式的话,kotlin提供了语言级别的支持,不需要自己实现,只需要将class替换为object即可。

    接口方面和Java没有区别。

    泛型

    泛型是通过不指定具体是数据类型来提升程序的扩展性的,一般在集合框架里面用到的比较多,比如List接口并没有规定需要保存的是何种数据类型,就是通过泛型来实现的,调用者可以自由的选择保存的数据类型。

    泛型还有一种用法是只定义一个泛型方法,这种情况只需要将泛型的定义写在方法上就可以了。kotlin还允许对泛型类型做出限制。

    类型擦除机制  

    Java的泛型是在JDK1.5版本中添加的,为了兼容性jvm语言的泛型通过类型擦除来实现,泛型信息在编译期经过类型检查以后会将类型信息擦除为Object,丢失了类型信息。

    那么问题来了,为什么通过反射可以获取到类型信息?

    答案是在类加载阶段,字节码中写死的泛型信息会被保存下来,实现了反射获取类型。

    public class Test<T> {
        private Test<Integer> test;
    
        private T item;
    }

    像是test的类型就可以通过反射获取,而item则不行,原因是它的类型在编译后为Object。

    逆变协变(通配符)

    kotlin的逆变和协变通过in和out关键字来实现,作用等同于Java的? extends T和?super T。

    由于泛型擦除机制的存在,不允许将子类的泛型对象赋值给一个父类的泛型对象,举个例子,List里面泛型为一个TextView,不能传入一个Button进去。

    这种需求的功能叫做协变,如果要实现这个功能可以使用?extends T 来声明泛型变量,规定泛型的上界,但是这样会引入新的限制,就是只能用它而不能修改它。这种特性也是kotlin选择out关键字的原因。

    相应的功能叫逆变,规定了泛型的下界,传入的类型必须是泛型类型或者其直接间接的父类,同时也引入了相反的限制,只能修改它但是不能使用他的功能。

    引入新限制的原因很简单,因为继承/实现时候子类必须实现父类/接口的方法,因此可以使用它,但是假如向其中传入了子类,在之后调用它的时候无法预见子类重写的方法会产生什么样的行为,导致类型不安全,因此不可以改变或者增加它。

    同理,在逆变中可以不能读取泛型中的方法,因为可能传入的是一个Object或者其他,没有T中的方法,但是可以传入它,因为可以将它看作一个Object。

    这就是pecs原则

    ?extends Object可以写作?,或者*(koklin)

    内联实化

    内联(inline)函数中的代码会在编译器自动替换到调用它的地方,再配合reified关键字修饰泛型,即可突破类型擦除的限制。

    举个实际了应用方法,调用Intent启动Activity的时候需要传入目的Activity的class对象,可以自定义startActivity来通过泛型启动Activity。

     有些时候需要传入参数也就是调用intent的putExtra函数。

    //TODO

    展开全文
  • 使用 Kotlin 特性实现的有限状态机 (FSM) 框架,基于事件驱动。 有限状态机定义 有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和...
  • 这篇文章主要是对MMKV进行封装,由此了解一些Kotlin特性,建议对着示例代码阅读文章,示例代码如下: MMKVDemo MMKV简单介绍 其实在MMKV的Wiki中已经有很详细的介绍了,地址如下: MMKV for Android官方Wiki MMKV是...

    这篇文章主要是对MMKV进行封装,由此了解一些Kotlin特性,建议对着示例代码阅读文章,示例代码如下:

    MMKVDemo

    MMKV简单介绍

    其实在MMKVWiki中已经有很详细的介绍了,地址如下:

    MMKV for Android官方Wiki

    MMKV是基于mmap内存映射key-value组件,底层序列化/反序列化使用protobuf实现,性能高稳定性强,而且Android这边还支持多进程

    单线程性能对比

    在这里插入图片描述

    • 写入性能

      MMKV远超于SharedPreferencesSQLite

    • 读取性能

      MMKVSharedPreferences相近,好于SQLite

    多进程性能对比

    在这里插入图片描述

    • 写入性能

      MMKV远超于MultiProcessSharedPreferencesSQLite

    • 读取性能

      MMKV远超于MultiProcessSharedPreferencesSQLite

    mmap简单介绍

    mmap是一种内存映射的方法,它可以将对象或者文件映射到地址空间,实现文件磁盘地址进程虚拟地址空间中的一段虚拟地址的一一对映关系,实现了这种映射关系后,进程可以采用指针的方式读写操作一段内存,而系统自动回写脏页面到对应的文件磁盘上,这样就完成了对文件的操作,而不需要再去调用writeread系统调用函数,同时内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间文件共享

    封装MMKV

    Preferences.kt,代码如下:

    package com.tanjiajun.mmkvdemo.utils
    
    import android.os.Parcelable
    import com.tencent.mmkv.MMKV
    import kotlin.properties.ReadWriteProperty
    import kotlin.reflect.KProperty
    
    /**
     * Created by TanJiaJun on 2020-01-11.
     */
    private inline fun <T> MMKV.delegate(
        key: String? = null,
        defaultValue: T,
        crossinline getter: MMKV.(String, T) -> T,
        crossinline setter: MMKV.(String, T) -> Boolean
    ): ReadWriteProperty<Any, T> =
        object : ReadWriteProperty<Any, T> {
            override fun getValue(thisRef: Any, property: KProperty<*>): T =
                getter(key ?: property.name, defaultValue)
    
            override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
                setter(key ?: property.name, value)
            }
        }
    
    fun MMKV.boolean(
        key: String? = null,
        defaultValue: Boolean = false
    ): ReadWriteProperty<Any, Boolean> =
        delegate(key, defaultValue, MMKV::decodeBool, MMKV::encode)
    
    fun MMKV.int(key: String? = null, defaultValue: Int = 0): ReadWriteProperty<Any, Int> =
        delegate(key, defaultValue, MMKV::decodeInt, MMKV::encode)
    
    fun MMKV.long(key: String? = null, defaultValue: Long = 0L): ReadWriteProperty<Any, Long> =
        delegate(key, defaultValue, MMKV::decodeLong, MMKV::encode)
    
    fun MMKV.float(key: String? = null, defaultValue: Float = 0.0F): ReadWriteProperty<Any, Float> =
        delegate(key, defaultValue, MMKV::decodeFloat, MMKV::encode)
    
    fun MMKV.double(key: String? = null, defaultValue: Double = 0.0): ReadWriteProperty<Any, Double> =
        delegate(key, defaultValue, MMKV::decodeDouble, MMKV::encode)
    
    private inline fun <T> MMKV.nullableDefaultValueDelegate(
        key: String? = null,
        defaultValue: T?,
        crossinline getter: MMKV.(String, T?) -> T,
        crossinline setter: MMKV.(String, T) -> Boolean
    ): ReadWriteProperty<Any, T> =
        object : ReadWriteProperty<Any, T> {
            override fun getValue(thisRef: Any, property: KProperty<*>): T =
                getter(key ?: property.name, defaultValue)
    
            override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
                setter(key ?: property.name, value)
            }
        }
    
    fun MMKV.byteArray(
        key: String? = null,
        defaultValue: ByteArray? = null
    ): ReadWriteProperty<Any, ByteArray> =
        nullableDefaultValueDelegate(key, defaultValue, MMKV::decodeBytes, MMKV::encode)
    
    fun MMKV.string(key: String? = null, defaultValue: String? = null): ReadWriteProperty<Any, String> =
        nullableDefaultValueDelegate(key, defaultValue, MMKV::decodeString, MMKV::encode)
    
    fun MMKV.stringSet(
        key: String? = null,
        defaultValue: Set<String>? = null
    ): ReadWriteProperty<Any, Set<String>> =
        nullableDefaultValueDelegate(key, defaultValue, MMKV::decodeStringSet, MMKV::encode)
    
    inline fun <reified T : Parcelable> MMKV.parcelable(
        key: String? = null,
        defaultValue: T? = null
    ): ReadWriteProperty<Any, T> =
        object : ReadWriteProperty<Any, T> {
            override fun getValue(thisRef: Any, property: KProperty<*>): T =
                decodeParcelable(key ?: property.name, T::class.java, defaultValue)
    
            override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
                encode(key ?: property.name, value)
            }
        }
    

    用法如下:

    package com.tanjiajun.mmkvdemo.ui.activity
    
    import android.os.Bundle
    import android.util.Log
    import androidx.appcompat.app.AppCompatActivity
    import com.tanjiajun.mmkvdemo.R
    import com.tanjiajun.mmkvdemo.data.model.UserData
    import com.tanjiajun.mmkvdemo.utils.*
    import com.tencent.mmkv.MMKV
    
    /**
     * Created by TanJiaJun on 2020-01-14.
     */
    class MainActivity : AppCompatActivity() {
    
        private val mmkv: MMKV by lazy { MMKV.defaultMMKV() }
    
        private var boolean by mmkv.boolean(key = "boolean", defaultValue = false)
        private var int by mmkv.int(key = "int", defaultValue = 0)
        private var long by mmkv.long("long", 0L)
        private var float by mmkv.float(key = "float", defaultValue = 0.0F)
        private var double by mmkv.double(key = "double", defaultValue = 0.0)
        private var byteArray by mmkv.byteArray(key = "byteArray")
        private var string by mmkv.string(key = "string")
        private var stringSet by mmkv.stringSet(key = "stringSet")
        private var parcelable by mmkv.parcelable<UserData>("parcelable")
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            boolean = true
            int = 100
            long = 100L
            float = 100F
            double = 100.0
            byteArray = ByteArray(100).apply {
                for (i in 0 until 100) {
                    set(i, i.toByte())
                }
            }
            string = "谭嘉俊"
            stringSet = HashSet<String>().apply {
                for (i in 0 until 100) {
                    add("第($i)个")
                }
            }
            parcelable = UserData(name = "谭嘉俊", gender = "男", age = 26)
    
            Log.i(TAG, "boolean:$boolean")
            Log.i(TAG, "int:$int")
            Log.i(TAG, "long:$long")
            Log.i(TAG, "float:$float")
            Log.i(TAG, "double:$double")
            Log.i(TAG, "byteArray:$byteArray")
            Log.i(TAG, "string:$string")
            Log.i(TAG, "stringSet:$stringSet")
            Log.i(TAG, "parcelable:$parcelable")
        }
    
        private companion object {
            const val TAG = "TanJiaJun"
        }
    
    }
    

    Kotlin特性

    挑几个语法讲解一下:

    内联函数

    示例代码中我创建几个内联代理函数,那什么是内联函数呢?为什么要用内联函数

    内联函数的原理是编译器把实现内联函数字节码动态插入到每次的调用处

    使用高阶函数会带来一些运行时效率损失,因为在Kotlin中,每一个函数都是一个对象,并且会捕获一个闭包,即那些在函数体内会访问到的变量。内存分配虚拟调用都会增加开销,在很多情况下,使用内联化Lambda表达式可以消除这类开销,举个例子,有这样一个函数:

    fun add(list: MutableList<String>, block: () -> String): String {
        list.add("谭嘉俊")
        return block()
    }
    

    然后是这样调用的:

    add(mutableListOf("MutableList")) { "谭嘉俊" }
    

    刚刚也说了,每一个函数都是一个对象,所以后面这段Lambda表达式它也是一个对象,所以调用的时候,其实它会调用block方法,Kotlin是基于JVM的编程语言,所以调用一个方法,其实就是将这个方法入栈的操作,调用结束后就会将这个方法出栈入栈出栈都会有性能的开销,所以我们可以使用内联函数,代码如下:

    inline fun add(list: MutableList<String>, block: () -> String): String {
        list.add("谭嘉俊")
        return block()
    }
    

    用上内联函数后,编译器就会将block方法里的代码内联到调用的地方,而不会再去调用block方法,从而减少了性能的开销,就像如下代码:

    inline fun add(list: MutableList<String>, block: () -> String): String {
        list.add("谭嘉俊")
        return "谭嘉俊"
    }
    

    crossinline

    示例代码中,我用crossinline修饰了gettersetter这两个参数,crossinline修饰符关键字,它要在内联函数中使用,可以禁止传递内联函数的Lambda表达式中的非局部返回

    那什么是非局部返回呢?在Kotlin中,我们只能对具名函数或者匿名函数使用非限定return来退出,所以我们在退出一个Lambda表达式就必须使用一个标签,并且在Lambda表达式内部禁止使用裸return,因为Lambda表达式不能使包含它的函数return,代码如下:

    fun function(block: () -> Unit) =
        print("谭嘉俊")
    
    fun add(list: MutableList<String>) {
        list.add("谭嘉俊")
        function {
            // 不能使add函数在此处return
            return
        }
    }
    

    但是如果Lambda表达式传给的函数是内联的,return也可以是内联的,代码如下:

    inline fun function(block: () -> Unit) =
        print("谭嘉俊")
    
    fun add(list: MutableList<String>) {
        list.add("谭嘉俊")
        function {
            // 可以使add函数在此处return
            return
        }
    }
    

    这种位于Lambda表达式中,但退出的是包含它的函数叫做非局部返回,就像我们经常用到的forEach就是个内联函数,代码如下:

    fun function(list: List<String>): Boolean {
        list.forEach {
            if (it == "谭嘉俊") return true // function函数return
        }
        return false
    }
    

    如果只是想局部返回forEach的话,可以像如下那样写:

    fun function(list: List<String>): Boolean {
        list.forEach {
            if (it == "谭嘉俊") return@forEach // 使用forEach隐式标签,局部返回到forEach
        }
        return false
    }
    

    一些内联函数可能调用的参数不是直接来自函数体,而是来自另一个执行上下文的Lambda表达式,例如:来自局部对象或者嵌套函数,在这种情况下,这个Lambda表达式中也不允许非局部返回,为了标识这种情况,这个Lambda表达式需要用crossinline修饰符标记,在上面的Preferences.kt文件中,getter参数和setter参数就用到crossinline修饰符,因为是局部对象ReadWritePropertygetValue方法和setValue方法调用了getter参数和setter参数,代码就不再贴出来了。

    具体化的类型参数

    示例代码中,我用到了Kotlinreified修饰符,在说这个之前,我们大概了解下Java泛型

    我们知道Java泛型是**”伪泛型“,它会在编译阶段进行类型擦除**。

    泛型类型擦除的原则有以下几点:

    • 擦除类型参数,即擦除**<>**和里面的内容。
    • 根据类型参数上下界推断并且替换成原生态类型,例如List原生态类型List
    • 为了保证类型安全,必要时插入强制类型转换代码。
    • Java编译器自动产生桥接方法来保证类型擦除后仍然具有泛型多态性

    类型参数无限制

    或者方法定义中的类型参数没有限制时,例如:或者<?>都被替换成Object示例代码如下:

    类型擦除前:

    public class Generic<T> {
    
        private T value;
        private List<?> list;
    
        public T getValue() {
            return value;
        }
    
        public void setValue(T value) {
            this.value = value;
        }
    
        public void setList(List<?> list) {
            this.list = list;
        }
    
    }
    

    类型擦除后:

    public class Generic {
    
        // T替换成Object
        private Object value;
        // List<?>替换成原生态类型List
        private List list;
    
        public Object getValue() {
            return value;
        }
    
        public void setValue(Object value) {
            this.value = value;
        }
    
        public void setList(List list) {
            this.list = list;
        }
    
    }
    

    类型参数有限制

    或者方法定义中的类型参数存在上界的时候,都被替换成它的上界,例如:<? extends Number>都会被替换成Number;当或者方法定义中的类型参数存在下界的时候,都被替换成它的下界,例如:<? super Number>会被替换成Object示例代码如下:

    类型擦除前:

    public class Generic<T extends Number> {
    
        private T value;
        private List<? extends Number> extendsList;
        private List<? super Number> superList;
    
        public T getValue() {
            return value;
        }
    
        public void setValue(T value) {
            this.value = value;
        }
    
        public void setExtendsList(List<? extends Number> extendsList) {
            this.extendsList = extendsList;
        }
    
        public void setSuperList(List<? super Number> superList) {
            this.superList = superList;
        }
    
    }
    

    类型擦除后:

    public class Generic {
    
        // <T extends Number>替换成Number
        private Number value;
        // <? extends Number>替换成Number
        private List<Number> extendsList;
        // <? super Number>替换成Object
        private List<Object> superList;
    
        public Number getValue() {
            return value;
        }
    
        public void setValue(Number value) {
            this.value = value;
        }
    
        public void setExtendsList(List<Number> extendsList) {
            this.extendsList = extendsList;
        }
    
        public void setSuperList(List<Object> superList) {
            this.superList = superList;
        }
    
    }
    

    以上就是Java泛型类型擦除的大概内容,现在说下Kotlinreified修饰符:

    reified修饰符可以保证泛型类型参数在运行时得到保留,要注意的是这个函数必须是内联函数,原理就是基于内联函数的工作机制,上面有提及到,每次调用带有reified的函数,编译器都知道这次调用中的泛型类型参数类型,然后就会生成对应的不同类型的类型实参字节码,并且动态插入到调用处,由于生成的字节码类型实参引用了具体类型,而不是类型参数,所以不会被编译器擦除。示例代码如下:

    内联函数startActivity:

    inline fun <reified T : AppCompatActivity> Activity.startActivity() =
        startActivity(Intent(this, T::class.java))
    

    调用处:

    startActivity<MainActivity>()
    

    反编译后的部分代码:

    startActivity:

    public static final void startActivity(@NotNull Activity $this$startActivity) {
       Intrinsics.checkParameterIsNotNull($this$startActivity, "$this$startActivity");
       Context var10003 = (Context)$this$startActivity;
       Intrinsics.reifiedOperationMarker(4, "T");
       $this$startActivity.startActivity(new Intent(var10003, AppCompatActivity.class));
    }
    

    调用处:

    // 被编译器替换成如下代码
    this.startActivity(new Intent((Context)this, MainActivity.class));
    

    要注意的是,Java代码不可以调用具体化的类型参数内联函数,但是可以调用失去内联特性的普通的内联函数,因为具体化类型参数得益于内联特性,上面也提到,这里不再赘述了。

    委托属性

    示例代码中,继承了ReadWriteProperty,并且实现了getValue方法和setValue方法,这里用到了Kotlin委托属性

    语法:val/var <属性名>: <类型> by <表达式>

    属性委托不必实现任何的接口,如果是var属性需要提供getValue方法和setValue方法,如果是val属性需要提供getValue方法,by后面的表达式就是该委托,属性对应的get()方法被委托给它的getValue方法,属性对应的set()的方法被委托给它的setValue方法。示例代码如下:

    class Delegate {
    
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String =
            "$thisRef, thank you for delegating '${property.name}' to me!"
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) =
            println("$value has been assigned to '${property.name}' in $thisRef.")
        
    }
    

    这里用到了operator修饰符,可以重载操作符,我们也可以实现ReadWriteProperty接口,它是用于实现读写属性委托的基本接口,这个只是为了方便我们实现委托属性,如果你有相同签名方法,就不必实现这个接口,代码如下:

    class Delegate : ReadWriteProperty<Any, String> {
    
        override fun getValue(thisRef: Any, property: KProperty<*>): String =
            "$thisRef, thank you for delegating '${property.name}' to me!"
    
        override fun setValue(thisRef: Any, property: KProperty<*>, value: String) =
            println("$value has been assigned to '${property.name}' in $thisRef.")
    
    }
    

    除了ReadWriteProperty外,还有另外一个接口:ReadOnlyProperty,这个是为了委托只读属性,只需要重写它的getValue方法就可以了。

    Kotlin标准库为几种委托提供了工厂方法,例如以下说的延迟属性Lazy就是其中一种:

    延迟属性Lazy

    调用延迟属性有这样的特征,第一次拿到属性的值(调用get()方法)会执行已传递给函数的Lambda表达式并且记录结果,后续调用get()方法只是返回记录的结果

    我们可以看下源码,提供了三个函数。

    lazy(initializer: () -> T)

    public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
    

    这个函数接受一个Lambda表达式,并且返回Lazy,并且调用SynchronizedLazyImpl函数,而且我们可以得知多个线程去调用这个lazy函数是安全的,代码如下:

    private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
        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 {
                        val typedValue = initializer!!()
                        _value = typedValue
                        initializer = null
                        typedValue
                    }
                }
            }
    
        override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
    
        override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
    
        private fun writeReplace(): Any = InitializedLazyImpl(value)
    }
    

    我们可以看到用的是**双重检查锁(Double Checked Locking)**来保证线程安全。

    lazy(mode: LazyThreadSafetyMode, initializer: () -> T)

    public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
        when (mode) {
            LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
            LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
            LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
        }
    

    这个函数接受两个参数,一个是LazyThreadSafetyMode,另外一个是Lambda表达式,并且返回LazyLazyThreadSafetyMode是个枚举类,代码如下:

    public enum class LazyThreadSafetyMode {
    
        SYNCHRONIZED,
        PUBLICATION,
        NONE,
    
    }
    

    使用SYNCHRONIZED可以保证只有一个线程初始化实例,实现细节在上面也说过了;使用PUBLICATION允许多个线程并发初始化值,但是只有第一个返回值用作实例的值;使用NONE不会有任何线程安全的保证以及的相关的开销,所以你如果你确认初始化总是发生在同一个线程的话可以用此模式,减少一些性能上的开销

    lazy(lock: Any?, initializer: () -> T)

    public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)
    

    这个函数接受两个参数,一个是你使用指定的对象(lock),目的是进行同步,另外一个是Lambda表达式,返回的是Lazy,调用的是SynchronizedLazyImpl函数,上面也说过,这里不再赘述了。

    我的GitHub:TanJiaJunBeyond

    Android通用框架:Android通用框架

    我的掘金:谭嘉俊

    我的简书:谭嘉俊

    我的CSDN:谭嘉俊

    展开全文
  • kotlin特性之run

    2021-04-25 15:48:20
    我们都知道链式编程,但今天所说的run就是应用于非链式的调用,非常适合用于给对象设置属性值,可以简化代码的编写。 以下举例说明: 不用run的情况: val makeText = Toast.makeText( this, ...

    我们都知道链式编程,但今天所说的run就是应用于非链式的调用,非常适合用于给对象设置属性值,可以简化代码的编写。
    以下举例说明:

    不用run的情况:

    val makeText = Toast.makeText(
    				this,
    				R.string.correct_toast,
    				Toast.LENGTH_SHORT
    				)
    makeText.setGravity(Gravity.TOP,getScreenWidth()/2,0)
    makeText.show()
    

    在上面的情况下必须要定义一个中间变量makeText 才能解决问题,十分啰嗦

    用run的情况:

    Toast.makeText(this,
     R.string.incorrect_toast,
     Toast.LENGTH_SHORT)
     .run {
         setGravity(Gravity.TOP,-getScreenWidth()/2,0)
         show()
     }
    

    在上面的情况下不需要定义一个中间变量makeText 就能解决问题

    展开全文
  • kotlin特性自定义DialogFragment

    千次阅读 2020-06-19 16:17:13
    title: kotlin特性自定义DialogFragment date: 2020-06-19 15:59:27 tags: [kotlin,安卓] 介绍 DSL(domain specific language),即领域专用语言:专门解决某一特定问题的计算机语言,比如大家耳熟能详的 SQL 和...
  • 标题栏是每个 App 都会有的控件,基本每个项目都会对标题栏进行封装。常见的方式是写一个标题栏的布局,用 标签添加到每一个页面中,然后在基类里提供初始化标题栏的方法。或者是实现一个标题栏的自定义控件,使用...
  • kotlin文件中 //封闭方法,不给Java调用,只能在kotlin中使用 fun `showTest`() { println("showTest") } // fun ` `(sex: Char) { println("sex:$sex") } fun `4325436465375`(name: String) { println(...
  • 之前有从基本语法和用法的角度聊过kotlin与java的关系,如果不熟悉可以参照:从java的角度看kotlin特性(一) 引言 从语法的角度来看,kotlin像是java的升级与增强,事实上,随着java版本的提高,现代语言的多种特性...
  • kotlin特性:如果前面编译器明确知道了类型,在后面就不用再进行无用的类型转换了,直接使用即可 //明确知道parent是Child的类型 val parent : Parent = Child() if ( parent is Child){ 直接使用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,620
精华内容 7,448
关键字:

kotlin特性