kotlin那些骚事儿

kotlin有哪些很爽的操作

属性代理 && 接口代理 && 运算符重载

先看一段代码

private val a by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
    2
}
官方:属性委托是委托属性的getter/setter方法,它允许委托对象在读取和写入值时插入执行一些中间操作。
看一下kotlin字节码反编译的java代码
@NotNull
private final Lazy a$delegate;
public final int getA() {
      Lazy var1 = this.a$delegate;
      Object var3 = null;
      boolean var4 = false;
      return ((Number)var1.getValue()).intValue();
}
构造方法里边{
     this.a$delegate = LazyKt.lazy((Function0)null.INSTANCE);
}
官方解释:属性对应的 get()(与 set())会被委托给它的 getValue() 与 setValue() 方法。 属性的委托不必实 现任何的接口,但是需要提供一个 getValue() 函数(与 setValue() ——————对于 var 属性
我们要自己写属性代理类就要 运算符重载 getValue()与setValue()方法

就是简单的访问a的时候去调用lazy对象的代码块并返回。

对象的初始化延迟到使用对象的时候,跟进一下lazy

  when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }
SynchronizedLazyImpl(initializer)就是我们熟知的DCL方式实现单例。
UnsafeLazyImpl(initializer) 就是没有锁机制的单例方式。
SafePublicationLazyImpl()它可以同时在多个线程中调用,并且可以在全部或部分线程上同时进行初始化。
但是,如果某个值已由另一个线程初始化,则将返回该值而不执行初始化。
--》具体方法源码不贴了。

我们可以利用属性委托做一些有趣操作比如

class PreDelegate<T>(
    private val name: String,
    private val default: T,
    private val isCommit: Boolean = false,
    private val sharedPreferences: SharedPreferences
) {
     operator fun getValue(thisRef: Any?, properties: KProperty<*>): T {
        return attain(properties.name, default) ?: default
    }

    operator fun setValue(thisRef: Any?, properties: KProperty<*>, value: T) {
        putIn(properties.name, value)
    }

    private fun attain(name: String, default: T): T? = with(sharedPreferences) {
        val result: Any? = when (default) {
            is Long -> getLong(name, default)
            is String -> getString(name, default)
            is Int -> getInt(name, default)
            is Boolean -> getBoolean(name, default)
            is Float -> getFloat(name, default)
            else -> throw IllegalArgumentException("this type is not supported")
        }
        result as? T
    }

    private fun putIn(name: String, value: T) {
        sharedPreferences.edit().apply {
            when (value) {
                is Long -> putLong(name, value)
                is String -> putString(name, value)
                is Int -> putInt(name, value)
                is Boolean -> putBoolean(name, value)
                is Float -> putFloat(name, value)
                else -> throw IllegalArgumentException("this type is not supported")
            }
            if (isCommit) {
                commit()
            } else apply()
        }
    }

}

class User{
    var name by PreDelegate("樊晨阳",sharedPreferences)
    var age by PreDelegate(12,sharedPreferences)
    
}
对User的读和写直接操作到SharedPreferences上。

上面说到重载getValue()与setValue()方法就要提一下kotlin的运算符重载

class Shoper(val age: Int) {
    operator fun plus(shoper: Shoper): Int {
        return age + shoper.age
    }
}

fun main() {
    val shoper3 = Shoper(3)
    val shoper4 = Shoper(4)
    println(shoper3 + shoper4)
}
// 输出:7
像c++一样,如果类没有重载运算符,直接使用编译器会直接报错。

当我们在想要委托接口的时候 👇👇👇👇👇👇👇👇

interface AAAA {
    fun show()
    fun help()
}

class BA : AAAA {
    override fun show() {
        println("快来!有人裸奔!!")
    }

    override fun help() {
        
    }

}

class UserDelegate(val instance: AAAA = BA()) : AAAA by instance {
    override fun help() {
        // 增强代码部分
        println("在哪 !!")
    }
}

fun main() {
    UserDelegate().show()
}
简便的委托模式,从语言支持开始。

中缀表达式

class Teacher {
    val name = "张三"
}

class Student {
    val name = "李四"
}
//infix 关键字标记
// 因为是中缀表达式所有 只可以有两个对象参与运算,一个作为receiver一个作为参数,返回值不受限
infix fun Teacher.teache(tar: Student) {
    println("$name 教 ${tar.name} 学习 !!")
}

fun main() {
    val teacher = Teacher()
    val student = Student()
    /*
    像写记叙文一样完成了编程 👇👇👇👇👇👇👇 🐂
    */
    teacher teache student
}
//输出:张三 教 李四 学习 !!

var && val && const

class Teacher {
    val name = "张三"
    val age
        get() = Math.random() * 100
}

fun main() {
    val teacher = Teacher()
    for (i in 0..10) {
        println(teacher.age)
    }
}
//定义在类外边 使用const 这叫:常量
const val age:Int = 100
当属性被声明为val也是可变的,但是在方法内定义了一个val 一般跟final没什么区别
val修饰的叫做只读变量  仍然是变量 只是在一定程度上减小变量的变化程度。

inline noinline crossinline关键字

inline fun helpMe(action: () -> Unit) {
    println("我开始干活了")
    action()
}

fun main() {
    helpMe {
        println("今天 我要搬 1000 块砖")
    }
}
//输出:
我开始干活了
今天 我要搬 1000 块砖
================================================
当我们不使用inline声明在函数前边main()函数java代码
public static final void main() {
      helpMe((Function0)null.INSTANCE);
}
public interface Function0<out R> : kotlin.Function<R> {
    public abstract operator fun invoke(): R
}
public static final void helpMe(@NotNull Function0 action) 
// 虽然kotlin支持函数类型但是在jvm上运行的时候就必须做SAM转换,代码块变为了匿名内部类,增加了函数的开销。但是用inline的时候 看一下转换后的代码
 public static final void main() {
      int $i$f$helpMe = false;
      String var1 = "我开始干活了";
      boolean var2 = false;
      System.out.println(var1);
      int var3 = false;
      String var4 = "今天 我要搬 1000 块砖";
      boolean var5 = false;
      System.out.println(var4);
   }
 public static void main(String[] var0) {
      main();
 }
//直接将两部分代码合并到了一个新的方法中 没有了对象的创建 减少了开销

说完了inline说一下noinline关键字 crossinline

var fast: () -> Unit = {}
inline fun work(noinline action: () -> Unit, action3: () -> Unit) {
    fast = action
    fast = action3  ------>报错  action3被内联表明只希望在内联方法调用处直接执行,不希望被存储。也可能是被内联了 也就不再被实例化为对象了,也就没办法被存储了。
}
//noinline action: () -> Unit -》禁止内敛action

fun hasZeros(ints: List<Int>): Boolean {
    ints.forEach {
        if (it == 0) return true // 从 hasZeros 返回  非局部返回:直接退出 hasZeros函数 forEach()函数必须为内联函数,,可以使用crossinline 来禁止内联函数某些字段非局部返回。
    }
    return false
}

待续。。。。

posted @ 2021-08-05 09:03  沙雕货  阅读(52)  评论(0编辑  收藏  举报