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
}