变量的定义以及类型

var 定义可变的变量,val 定义不可变的变量,var 和 val会自动推导类型,var 一旦确定类型,就不能赋值其他类型的值. 在变量名:类型 来声明具体类型的变量

  1. var age = 18 // 类型推断
  2. // age = "12" 一旦确定类型 就不能更改类型
  3. val name = "kotlin"
  4. const val name = "kotlin" // 编译期的常量 等价于 final
  5. // val world = World()
  6. var j:String = "123"

kotlin 中方法的定义:

  1. fun doubleNumber(x: Int): Int {
  2. return x * 2
  3. }

kotlin 中和Java中的public private protect 三种设置一致。

在Kotlin 语言体系中一切皆对象,没有像Java类似的原始类型和包装类型
image.png

空安全机制

? 表示可空类型,!! 表示强制调用,会存在空指针异常。

lateinit 延后初始化,不可空,不能有初始值.

?. 表示如果不为空则调用方法,如果为空则不调用

  1. // 延迟初始化
  2. private lateinit var et_username: EditText
  3. // 可空变量
  4. private var et_password: EditText? = null
  5. // 不可空时调用
  6. et_password?.text = Editable.Factory.getInstance().newEditable(CacheUtils.get(passwordKey))
  7. // 强制调用
  8. et_password!!.text = Editable.Factory.getInstance().newEditable(CacheUtils.get(passwordKey))

Kotlin 平台类型,无法做到是否为空的判断,因为在Java平台如果标记了@Nullable 那么kotlin就可以判断是否为空了

  1. // kotlin 平台类型 无法做到是否为空的判断,
  2. // 如果Java标记@Nullable kotlin就可以判断是否可空了 Button? CodeView!
  3. //delegate.findViewById 标记了@Nullable 所以默认Button?
  4. val btn_login:Button? = delegate.findViewById<Button>(R.id.btn_login)
  5. // findViewById 在Java平台没有标记@Nullable注解,所以不能判空,强制设置不为空CodeView!
  6. // 如果为空则报空指针异常
  7. val img_code:CodeView! = findViewById<CodeView>(R.id.code_view)

如果将强制调用符用在结尾 CacheUtils.get(passwordKey)!! 表示会做非空判断,如果是null抛出异常,不是null则正常返回

类型判断与强转

is 关键字判断类型,as 关键字进行强转。

  1. // is 判断类型
  2. if (view is CodeView) {
  3. // as 进行强制转换,因为通过了is判断,可以直接调用
  4. // val codeView = view as CodeView
  5. // 可以直接调用,智能判断类型,不需要强转
  6. view.updateCode()
  7. } else if (view is Button) {
  8. login()
  9. }

获取kotlin类和Java类

通过类名::class获取kotlin类型,通过类名::class.java 获取Java类型

  1. // kotlin获取class对象:LessonActivity::class 注意这是kotlin的class对象
  2. // 要获取Java的对象需要LessonActivity::class.java
  3. startActivity(Intent(this, LessonActivity::class.java))

kotlin 中的静态变量与单例

顶层函数

  1. kotlin 可以直接定义文件,不用像Java一样定义类,kotlin文件中定义的函数称为顶层函数也叫包级函数,在文件中定义的变量和方法,文件中的变量和方法可以直接访问。
  2. @file:JvmName() 改变文件名,如果不改变那么Java访问kotlin文件需要 文件名Kt.xx 需要在文件名后面加上Kt.
  1. @file:JvmName("Utils") //改变文件名,否则Java默认调用UtilsKt.xx
  2. //改变之后可以通过 Utils.xx调用
  3. package com.example.core.utils
  4. import android.content.res.Resources
  5. import android.util.TypedValue
  6. import android.widget.Toast
  7. import com.example.core.BaseApplication
  8. // 静态变量 第一种:定义在文件中 顶层函数也叫包级函数
  9. private val displayMetrics = Resources.getSystem().displayMetrics
  10. fun dp2px(dp: Float): Float {
  11. return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics)
  12. }
  13. fun toast(string: String) {
  14. toast(string, Toast.LENGTH_SHORT)
  15. }
  16. fun toast(string: String, duration: Int) {
  17. Toast.makeText(BaseApplication.currentApplication(), string, duration).show()
  18. }

kotlin 中调用:

  1. import com.example.core.utils.toast
  2. toast("用户名不合法")

Java 中的调用:@file:JvmName("Utils") //改变文件名,否则Java默认调用UtilsKt.xx

  1. import com.example.core.utils.Utils;
  2. public class Test {
  3. public static void main(String[] args) {
  4. Utils.toast("");
  5. }
  6. }

单例

kotlin中实现单例非常简单,通过object关键字就可以定义单例类,kotlin中调用 类名.xxx()类似静态变量的调用方式,Java中的调用 类名.INSTANCE.xx().

  1. //kotlin 第二种静态变量的方式:和第一种的顶层不一样,通过类名.xx方式调用
  2. //object 实现了一个单例类,通过单例对象来访问的,Java中需要:类名.INSTANCE.xx
  3. @SuppressLint("StaticFieldLeak")
  4. object CacheUtils {
  5. @SuppressLint("StaticFieldLeak")
  6. val context: Context = BaseApplication.currentApplication()
  7. val SP: SharedPreferences =
  8. context.getSharedPreferences(context.getString(R.string.app_name), Context.MODE_PRIVATE)
  9. fun save(key: String, value: String) {
  10. SP.edit().putString(key, value).apply()
  11. }
  12. fun get(key: String): String? {
  13. return SP.getString(key, null)
  14. }
  15. }

伴生对象

kotlin中的伴生对象,其实和Java中的类的静态变量static类似,在某个类内部定义静态变量。@JvmStatic 可以是Java调用类似:类名.xx 否则在Java中就是 类名.Compone.xx()

  1. class BaseApplication : Application() {
  2. override fun attachBaseContext(base: Context?) {
  3. super.attachBaseContext(base)
  4. currentApplication = this
  5. }
  6. // 伴生对象,在某个类的内部定义静态变量
  7. companion object {
  8. private lateinit var currentApplication: Context
  9. // 变成Java中的静态方法 类名.xxx
  10. @JvmStatic
  11. fun currentApplication(): Context {
  12. return currentApplication
  13. }
  14. }
  15. }

简化上述的调用方式,可以直接减少方法:

  1. class BaseApplication : Application() {
  2. override fun attachBaseContext(base: Context?) {
  3. super.attachBaseContext(base)
  4. currentApplication = this
  5. }
  6. // 伴生对象,在某个类的内部定义静态变量
  7. companion object {
  8. @JvmStatic //方便Java 实现静态变量的方式调用
  9. // @get:JvmName 方便Java调用
  10. @get:JvmName("currentApplication")
  11. lateinit var currentApplication: Context
  12. private set // 不能调用set
  13. // // 变成Java中的静态方法 类名.xxx
  14. // @JvmStatic
  15. // fun currentApplication(): Context {
  16. // return currentApplication
  17. // }
  18. }
  19. }

构造器和set/get

构造器

kotlin 中的构造函数实现方式有两种:一种通过constructor声明构造函数,一种是通过在类名(val str:String)类似

  1. // kotlin所有的类 继承Any open class 表示可以被继承
  2. class User : Any {
  3. // kotlin 默认构造函数
  4. constructor()
  5. // 可以声明多个构造函数
  6. constructor(username: String?, password: String?, code: String?) {
  7. this.username = username
  8. this.password = password
  9. this.code = code
  10. }
  11. var username: String? = null
  12. var password: String? = null
  13. var code: String? = null
  14. }

第二种方式简化了构造函数 主构造器,如果构造函数中不需要特别复杂的业务可以不用进行上述的constructor,直接在类上声明. 通过var和val声明的变量,都可以被外部调用,称为构造属性,直接写变量名那么外部不可以调用。

  1. class Person(var string: String,val s:String, age: Int) {
  2. }
  3. val p = Person("1","2",1)
  4. p.string = "2"
  5. // p.age 外部无法调用
  6. open class User constructor() {
  7. // kotlin声明构造器
  8. constructor(username: String?, password: String?, code: String?) : this() {
  9. this.username = username
  10. this.password = password
  11. this.code = code
  12. }
  13. }

get/set

kotlin 默认实现了get和set方法,也可以自定义实现get/set方法进行如下调用:

  1. // kotlin将setter和getter和属性关联在一起了,默认已经实现了
  2. var code: String? = null
  3. // set(value) {
  4. // field = value
  5. // }
  6. // get() {
  7. // return field
  8. // }

kotlin 可以直接调用 xx.code = xxx; xx.code,Java 需要使用setXX()getXX(),要想Java直接使用xx.code的方式 @JvmField 生成一个公开的成员变量 在Java中可以直接访问不用get/set了

  1. @JvmField //生成一个公开的成员变量 在Java中可以直接访问不用get/set了
  2. var username: String? = null
  3. var password: String? = null

继承与实现接口

kotlin的继承的实现: 类名: 继承类(),接口类,接口类...,但子类实现构造函数时,继承类的变化如下: 构造方法通过:super()调用继承类的构造方法,通过:this() 调用其他的构造方法 Kotlin 接口的两个特性:接口属性、接口方法默认实现

  1. class MainActivity : AppCompatActivity(), View.OnClickListener{
  2. }
  3. // 构造函数实现,可以在继承某个类时不用写()
  4. class CodeView : AppCompatTextView {
  5. constructor(context: Context) : this(context, null)
  6. constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
  7. setTextSize(TypedValue.COMPLEX_UNIT_SP, 18f);
  8. gravity = Gravity.CENTER;
  9. setBackgroundColor(getContext().getColor(R.color.colorPrimary));
  10. setTextColor(Color.WHITE);
  11. paint.isAntiAlias = true;
  12. paint.style = Paint.Style.STROKE;
  13. paint.color = getContext().getColor(R.color.colorAccent);
  14. paint.strokeWidth = dp2px(6f);
  15. updateCode();
  16. }
  17. }

如果某个类需要被继承,需要添加open关键字

  1. open class User : Any {
  2. }

简化继承:

  1. class Person(var string: String, val s: String, age: Int) : User(string, s, age.toString()) {
  2. }

匿名内部类

kotlin 可以通过object关键字创建单例对象,那么匿名内部类也是使用object:

  1. // kotlin中创建一个匿名内部类,使用的object关键字 object:
  2. call.enqueue(object : Callback {
  3. override fun onFailure(call: Call, e: IOException) {
  4. TODO("Not yet implemented")
  5. }
  6. override fun onResponse(call: Call, response: Response) {
  7. TODO("Not yet implemented")
  8. }
  9. })

kotlin可以在字符串””中设置模板字符串${}

when/in 关键字

in 关键字 支持表达式,kotlin中没有强制捕获异常

  1. when (response.code()) {
  2. in 200..299 -> {
  3. val body = response.body()
  4. var json: String? = null
  5. try {
  6. json = body?.string()
  7. } catch (e: IOException) {
  8. e.printStackTrace()
  9. }
  10. entityCallback.onSuccess(convert(json, type))
  11. }
  12. in 400..499 -> {
  13. entityCallback.onFailure("客户端错误")
  14. }
  15. in 501..599 -> {
  16. entityCallback.onFailure("服务器错误")
  17. }
  18. else -> {
  19. entityCallback.onFailure("未知错误")
  20. }
  21. }

kotlin中定义抽象类和Java中的类似都是通过abstract关键字

when 也可以进行值的返回:

  1. var colorRes = R.color.playback
  2. colorRes = when (state) {
  3. Lesson.State.PLAYBACK -> R.color.playback
  4. Lesson.State.LIVE -> R.color.live
  5. Lesson.State.WAIT -> R.color.wait
  6. }

kotlin 简化判空操作符

kotlin中没有三元表达式,kotlin可以使用?:的形式CacheUtils.get(passwordKey) ?: "123"

  1. setText(R.id.tv_date, lesson.date ?: "日期待定")

枚举类

枚举:就是一组有限数量的值

  1. enum class Human {
  2. MAN, WOMAN
  3. }
  4. fun isMan(data: Human) = when (data) {
  5. Human.MAN -> true
  6. Human.WOMAN -> false
  7. }

枚举中的值它的结构引用都相等,每个枚举值值内存中都是同一个对象引用

  1. // 枚举引用和地址都相等
  2. println(Human.WOMAN == Human.WOMAN) // true
  3. println(Human.WOMAN === Human.WOMAN)//true

如果想要枚举值拥有不一样的对象引用,该如何实现呢?就是密封类

密封类

密封类是更强大的枚举类,密封类用来表示受限的类继承结构,是枚举类的扩展,枚举类型,每个枚举常量只存在一个实例,而密封类的子类可以有可包含状态的多个实例

  1. sealed class Expr
  2. data class Const(val number: Double) : Expr()
  3. data class Sum(val el: Expr, val e2: Expr) : Expr()
  4. object NotANumber : Expr()
  5. fun eval(expr: Expr): Double = when (expr) {
  6. is Const -> expr.number
  7. is Sum -> eval(expr.el) + eval(expr.e2)
  8. NotANumber -> Double.NaN
  9. }
  10. fun main() {
  11. println(eval(Const(123.4)))
  12. println(eval(Sum(Const(1.2), Const(1.3))))
  13. }

密封类也经常用在 网络请求的结果封装中:

  1. sealed class Result<out R> {
  2. data class Success<out T>(val data: T, val message: String = "") : Result<T>()
  3. data class Error(val error: Exception) : Result<Nothing>()
  4. data class Loading(val time: Long = System.currentTimeMillis()) : Result<Nothing>()
  5. }
  6. fun display(data: Result<*>) = when (data) {
  7. is Result.Success<*> -> "请求成功"
  8. is Result.Error -> "请求失败"
  9. is Result.Loading -> "请求中..."
  10. }
  11. //模拟网络请求
  12. display(Result.Success("success"))
  13. display(Result.Error(RuntimeException("e")))
  14. display(Result.Loading())

获取外部引用

this@LessonPresenter.lessons

  1. private var lessons = ArrayList<Lesson>()
  2. private val type = object : TypeToken<List<Lesson>>() {}.type
  3. fun fetchData() {
  4. HttpClient.get(LESSON_PATH, type, object : EntityCallback<List<Lesson>> {
  5. override fun onSuccess(@NonNull lessons: List<Lesson>) {
  6. this@LessonPresenter.lessons = lessons as ArrayList<Lesson>
  7. activity.runOnUiThread { activity.showResult(lessons); }
  8. }
  9. override fun onFailure(@Nullable message: String?) {
  10. activity.runOnUiThread {
  11. toast(message ?: "网络异常")
  12. }
  13. }
  14. })
  15. }
  16. fun showPlayback() {
  17. val playbackLessons = ArrayList<Lesson>()
  18. for (lesson in lessons) {
  19. if (lesson.state == Lesson.State.PLAYBACK) {
  20. playbackLessons.add(lesson);
  21. }
  22. }
  23. activity.showResult(playbackLessons);
  24. }

嵌套内部类

kotlin 普通的内部类是静态内部类,无法访问外部类。通过inner关键声明为内部类就可以访问。kotlin 设计和Java相反,在Java中默认是嵌套内部类,而在kotlin中默认是静态内部类,静态内部类的好处:不会持有外部类的引用规避了内存泄漏的风险,而在Java中默认是内部类持有外部类的引用,如果不注意那么会很容易造成内存泄漏的风险。Kotlin 将默认犯错的风险抹掉了。

  1. // kotlin 中普通的嵌套内部类 是静态内部类
  2. class A {
  3. val name: String = ""
  4. fun foo() {
  5. println("foo")
  6. }
  7. // 静态内部类
  8. class B {
  9. // println(name) 静态内部类 无法访问外部类
  10. // foo()
  11. }
  12. //C 类是A类的内部类,可以访问A类的成员属性和方法
  13. inner class C {
  14. fun f() {
  15. foo()
  16. println(name)
  17. }
  18. }
  19. }

kotlin中静态内部类,使用internal 关键字为可见修饰符,可以做到模块内可访问。

  • private 意味着只在这个类内部(包含其所有成员)可见;
  • protected—— 和 private一样 + 在子类中可见。
  • internal —— 能见到类声明的 本模块内 的任何客户端都可见其 internal 成员;
  • public —— 能见到类声明的任何客户端都可见其 public 成员。
  1. class LessonAdapter : RecyclerView.Adapter<LessonAdapter.LessonViewHolder>() {
  2. private var list: List<Lesson> = ArrayList<Lesson>()
  3. fun updateAndNotify(list: List<Lesson>) {
  4. this.list = list
  5. notifyDataSetChanged()
  6. }
  7. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LessonViewHolder {
  8. return LessonViewHolder.onCreate(parent)
  9. }
  10. override fun onBindViewHolder(holder: LessonViewHolder, position: Int) {
  11. holder.onBind(list[position])
  12. }
  13. override fun getItemCount(): Int {
  14. return list.size
  15. }
  16. // internal constructor internal后面必须有constructor关键字
  17. class LessonViewHolder internal constructor(itemView: View) : BaseViewHolder(itemView) {
  18. // internal 静态内部类 可以有伴生对象,如果inner class嵌套内部类 就不允许存在伴生对象
  19. companion object {
  20. fun onCreate(parent: ViewGroup): LessonViewHolder {
  21. return LessonViewHolder(
  22. LayoutInflater
  23. .from(parent.context)
  24. .inflate(R.layout.item_lesson, parent, false)
  25. )
  26. }
  27. }
  28. fun onBind(lesson: Lesson) {
  29. var date = lesson.date
  30. if (date == null) {
  31. date = "日期待定";
  32. }
  33. setText(R.id.tv_date, date)
  34. //当content不为空 调用let 抽离对象的属性
  35. lesson.content?.let { setText(R.id.tv_content, it) }
  36. val state = lesson.state
  37. if (state != null) {
  38. setText(R.id.tv_state, state.stateName());
  39. var colorRes = R.color.playback
  40. when (state) {
  41. Lesson.State.PLAYBACK -> colorRes = R.color.playback
  42. Lesson.State.LIVE -> colorRes = R.color.live
  43. Lesson.State.WAIT -> colorRes = R.color.wait
  44. }
  45. val backgroundColor = itemView.getContext().getColor(colorRes);
  46. getView<View>(R.id.tv_state).setBackgroundColor(backgroundColor);
  47. }
  48. }
  49. }
  50. }

初始化操作

注意:成员变量的初始化,要在init上面,因为kotlin会根据文件的顺序一起塞到构造函数内

  1. init {
  2. setTextSize(TypedValue.COMPLEX_UNIT_SP, 18f)
  3. gravity = Gravity.CENTER
  4. setBackgroundColor(getContext().getColor(R.color.colorPrimary))
  5. setTextColor(Color.WHITE)
  6. paint.isAntiAlias = true
  7. paint.style = Paint.Style.STROKE
  8. paint.color = getContext().getColor(R.color.colorAccent)
  9. paint.strokeWidth = dp2px(6f)
  10. updateCode()
  11. }

数据类

在kotlin == 表示equals方法的实现,=== 表示对比的是地址值,数据类对应着Java中的Bean类,data class 可以实现解构。在kotlin中会将数据类自动生成一些有用的方法equals、hashCode、toString、copy、componentN

  1. data class User(@JvmField var username: String?, var password: String?, var code: String?) {
  2. // kotlin声明构造器
  3. constructor() : this(null, null, null) {
  4. }
  5. }
  6. val (username, password, code) = User()
  7. val u1 = User()
  8. val copy = u1.copy()
  9. print(u1==copy)//调用equals
  10. print(u1===copy)//比较地址

扩展函数/属性

  1. // 扩展函数 this表示自身
  2. fun Float.dp2px(): Float {
  3. return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, displayMetrics)
  4. }
  5. // 扩展属性
  6. val ViewGroup.first: View
  7. get() = getChildAt(0)

内联函数

kotlin 通过关键字inline 声明内联函数,直接将函数的内容复制到调用的地方,大大的减少调用栈的入栈和出栈操作。

  1. inline fun doubleNumber(x: Int): Int {
  2. return x * 2
  3. }

但是如果大量的声明内联函数,会使编译器编译时变慢,内联函数中的代码不能过多。而且编辑器会提示警告:此处使用inline并不能带来性能的提升,那么如何正确的使用inline呢?
Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types
image.png

inline 主要用在函数参数时函数类型时进行设置,为什么呢?这就需要理解kotlin中的函数类型的本质

函数类型

kotlin 可以将参数作为函数类型传递,也就是经常所说的函数式编程,参数设置函数类型(参数类型)->返回类型

  1. class View {
  2. }
  3. fun click(view: View) {
  4. println("函数被执行")
  5. }
  6. // kotlin 声明函数类型(参数类型)->返回类型
  7. fun setOnClickListener(listener: (View) -> Unit) {
  8. // 调用函数
  9. println("调用函数,此处进行函数的传参")
  10. listener(View())
  11. }

当调用setOnClickListener函数,可以给参数传递函数:通过::click作为参数的传递

  1. //传递一个函数
  2. setOnClickListener(::click)

同时可以传递匿名函数:

  1. //可以使用lambal 表达式 匿名函数
  2. setOnClickListener {
  3. //listener(View()) it == View()
  4. println("直接执行 view$it")
  5. }

那么在Java中如何调用Kotlin的函数类型的呢?kotlin在编译时将函数类型,改成了Java中的接口

  1. HelloKt.setOnClickListener(new Function1<View, Unit>() {
  2. @Override
  3. public Unit invoke(View view) {
  4. return null;
  5. }
  6. });
  7. // Functions.kt
  8. public interface Function1<in P1, out R> : Function<R> {
  9. /** Invokes the function with the specified argument. */
  10. public operator fun invoke(p1: P1): R
  11. }

那么kotlin将函数类型,设置成了一个通用的接口,那么反编译Java后就需要创建接口对象,调用次数频繁就会创建很多对象,会非常损耗性能。上述讲的inline内联函数,可以解决kotlin函数类型性能的问题。

先看没有使用lnline之前:每次调用setOnClickListener都会创建对象

  1. setOnClickListener((Function1)null.INSTANCE);
  2. setOnClickListener((Function1)null.INSTANCE);

来看使用inline之后

  1. // kotlin 声明函数类型(参数类型)->返回类型
  2. inline fun setOnClickListener(listener: (View) -> Unit) {
  3. // 调用函数
  4. println("调用函数,此处进行函数的传参")
  5. listener(View())
  6. }

编译后的结果:直接将代码赋值过来了,没有在去创建对象

  1. public static final void main() {
  2. int $i$f$setOnClickListener = false;
  3. String var1 = "调用函数,此处进行函数的传参";
  4. System.out.println(var1);
  5. View p1 = new View();
  6. int var3 = false;
  7. click(p1);
  8. }

:::tips 注意:inline 不仅可以减少调用栈的次数,还可以用在函数的参数是函数类型时,减少创建对象的次数。 :::

inline + reified 简化泛型类型逻辑

使用reified处理后的kotlin方法无法被java代码调用。 reified 必须和 inline进行搭配使用,reified 使抽象的东西更具体更真实,类似泛型T 抽象的类型

  1. interface API {
  2. @GET("lessons")
  3. fun lessons(): Call<Any>
  4. }
  5. val RETROFIT = Retrofit.Builder().baseUrl("https://api.hencoder.com")
  6. .build()
  7. fun <T> create(clazz: Class<T>): T {
  8. return RETROFIT.create(clazz)
  9. }
  10. // 通过inline + reified 优化调用 inline可以直接将create代码copy到调用的地方 在通过reified 推断调用的类型
  11. inline fun <reified T> create2(): T {
  12. return RETROFIT.create(T::class.java)
  13. }
  14. fun main() {
  15. // 常规的写法
  16. print(create(API::class.java).lessons())
  17. // 这样写就简单很多 只需要指定泛型,而不需要指定class
  18. print(create2<API>().lessons())
  19. }

委托 by

by lazy 内部的代码块只会加载一次,只有在初次调用的使用才会调用代码块中的代码。 by 关键字将 统一的操作委托给一个统一的对象处理。

  1. // by lazy 只会调用一次,并且只有在初次调用的时候才会创建对象,声明时不会创建对象,大大简化了代码
  2. private val lessonPresenter by lazy {
  3. // 最后一行默认return
  4. LessonPresenter(this)
  5. }

by 委托代理,是kotlin的核心特性之一,那么by它可以应用到那些场景呢?
例如:要在应用中存储token信息,常规的写法:

  1. var token: String
  2. set(value) {
  3. CacheUtils.save("token", value)
  4. }
  5. get() {
  6. return CacheUtils.get("token")!!
  7. }

那么如果 要存储多个token信息的话,代码就变成了火葬场:

  1. var token: String
  2. set(value) {
  3. CacheUtils.save("token", value)
  4. }
  5. get() {
  6. return CacheUtils.get("token")!!
  7. }
  8. var token2: String
  9. set(value) {
  10. CacheUtils.save("token", value)
  11. }
  12. get() {
  13. return CacheUtils.get("token")!!
  14. }
  15. .......

相同的操作,确要重复写多次,by 就是用来统一处理相同的操作逻辑,通过代理对象来简化处理:委托类需要实现两个方法getValue setValue,当然如果val 只需要实现getValue方法即可

  1. // 通过委托的方式 创建委托类
  2. class Saver(private val key: String) {
  3. operator fun getValue(lessonActivity: LessonActivity, property: KProperty<*>): String {
  4. return CacheUtils.get(key)!!
  5. }
  6. operator fun setValue(
  7. lessonActivity: LessonActivity,
  8. property: KProperty<*>,
  9. value: String
  10. ) {
  11. CacheUtils.save(key, value)
  12. }
  13. }

使用委托类的:by 委托对象

  1. var token: String by Saver("token")
  2. // 加入有另一个token 也需要进行存储和读取操作,就可以使用委托的方式统一实现 他太棒了
  3. var token2: String by Saver("token2")

委托的更多用法:

  1. class byTest {
  2. //属性委托
  3. val a: Int = 0
  4. val count: Int by ::a //::a是属性的引用
  5. //懒加载委托
  6. val data: String by lazy {
  7. request()
  8. }
  9. fun request(): String {
  10. println("网络请求")
  11. return "请求结果"
  12. }
  13. }
  14. // 自定义委托的方式1
  15. class StringDelegate(private var s: String = "hello") {
  16. operator fun getValue(thisRef: Owner, property: KProperty<*>): String {
  17. return s
  18. }
  19. operator fun setValue(thisRef: Owner, property: KProperty<*>, value: String) {
  20. s = value
  21. }
  22. }
  23. // 自定义委托的方式2 实现ReadWriteProperty来进行自定义委托
  24. class StringDelegate2(private var s: String = "hello") : ReadWriteProperty<Owner, String> {
  25. override fun getValue(thisRef: Owner, property: KProperty<*>): String {
  26. return s
  27. }
  28. override fun setValue(thisRef: Owner, property: KProperty<*>, value: String) {
  29. s = value
  30. }
  31. }
  32. //提供委托 provideDelegate来实现 在属性委托之前做一些额外的工作
  33. class SmartDelegate : PropertyDelegateProvider<Owner, ReadWriteProperty<Owner, String>> {
  34. override operator fun provideDelegate(
  35. thisRef: Owner,
  36. prop: KProperty<*>
  37. ): ReadWriteProperty<Owner, String> {
  38. //根据属性名 来进行委托的变更
  39. return if (prop.name.contains("log")) {
  40. StringDelegate2("log")
  41. } else {
  42. StringDelegate2("normal")
  43. }
  44. }
  45. }
  46. class Owner {
  47. var text: String by StringDelegate()
  48. var logText: String by SmartDelegate()
  49. var normalText: String by SmartDelegate()
  50. }
  51. // 利用委托实现可见性的封装
  52. class Model {
  53. //data的修改权 留在内部
  54. private val _data: MutableList<String> = mutableListOf()
  55. //只读的data 对外暴露
  56. val data: List<String> by ::_data
  57. fun load() {
  58. _data.add("hello")
  59. }
  60. }
  61. // 数据与view绑定
  62. operator fun TextView.provideDelegate(value: Any?, property: KProperty<*>) =
  63. object : ReadWriteProperty<Any?, String?> {
  64. override fun getValue(thisRef: Any?, property: KProperty<*>): String? = text.toString()
  65. override fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) {
  66. text = value
  67. }
  68. }
  69. fun main() {
  70. // val textView = TextView(this)
  71. // var message: String? by textView
  72. // message = "123"
  73. // textView.text = "456"
  74. // println(message)
  75. }

作用域函数

apply 特别适合对象的附加操作,特别适合在初始化操作。apply 返回自身

  1. private val paint = Paint().apply {
  2. isAntiAlias = true
  3. style = Paint.Style.STROKE
  4. color = getContext().getColor(R.color.colorAccent)
  5. strokeWidth = 6f.dp2px()
  6. }

let 的需要主动return返回最后一行,let作用域内部是it和apply比起来不优雅,let 非常适合空判断的情况also 是返回对象的本身其他的和let没有区别

  1. val state = lesson.state?.let {
  2. setText(R.id.tv_state, it.stateName())
  3. var colorRes = R.color.playback
  4. colorRes = when (it) {
  5. Lesson.State.PLAYBACK -> R.color.playback
  6. Lesson.State.LIVE -> R.color.live
  7. Lesson.State.WAIT -> R.color.wait
  8. }
  9. val backgroundColor = itemView.context.getColor(colorRes);
  10. getView<View>(R.id.tv_state).setBackgroundColor(backgroundColor);
  11. return@let it
  12. }

run 和 let类似,只不过内部作用域是this不是it,默认不返回自身

  1. val recyclerView = findViewById<RecyclerView>(R.id.list).run {
  2. layoutManager = LinearLayoutManager(this@LessonActivity)
  3. adapter = lessonAdapter
  4. addItemDecoration(DividerItemDecoration(this@LessonActivity, LinearLayout.VERTICAL))
  5. return@run this
  6. }
  7. val recyclerView = findViewById<RecyclerView>(R.id.list).run {
  8. layoutManager = LinearLayoutManager(this@LessonActivity)
  9. adapter = lessonAdapter
  10. addItemDecoration(DividerItemDecoration(this@LessonActivity, LinearLayout.VERTICAL))
  11. return@run this
  12. }
  13. refreshLayout = findViewById<SwipeRefreshLayout>(R.id.swipe_refresh_layout).run{
  14. this.setOnRefreshListener { lessonPresenter.fetchData() }
  15. this.isRefreshing = true
  16. return@run this
  17. }

run 和 apply 作用域都是this,apply返回自身,run返回最后一行 let 和 also 作用域都是it, also 返回自身,let 返回最后一行