目录

Kotlin常用技巧整理(优雅代码必备,持续更新)

http://image.catbro.cn/7385338374fc2.png

  • 更多分享:www.catbro.cn
  • 主要整理Kotin日常使用中的一些小技巧

1、可选参数

  • 用过C++等语言的同学对于默认参数应该非常熟,可选可选参数使得方法调用更加灵活,而不必传递 null 或默认值。而Java设计之初即没有该特性,Kotlin引入了该特性;如果用Java写估计你得写两个相同名称的方法而参数不同,也就是所谓的函数重载

      fun doSomething(what: String, time: Int = 0){
         ...
      }
    
      doSomething("eat")
      doSomething("eat", 1000)
    

2、Static Layout Import 静态布局变量导入

  • Android 中最常用的代码之一是使用 findViewById() 来获取对应 View。当然第三方也有一些解决方案,如 Butterknife 库,可以节省很多代码;
  • Kotlin 允许我们从一个导入的布局导入对视图的所有引用,可直通过xml中控件的Id名称直接使用该控件的实例对象,现在在Activity中和Fragment中都可以很方便的使用,节约了大量的代码。

3、lazy(懒加载)

为什么要用懒加载:

  • 1、启动速度提升:非启动时必须要初始化的资源通过懒加载可以加快应用的启动速度,特别像大图像、视频、音频、数据库驱动等;
  • 2、减少内存占用:只有真正使用该对象或资源时再去加载,减少内存的占用;

懒加载的使用方式:

  • lazy初始化两种方式

      val lazyString: String by lazy {
          "JayGoo"
      }
    
      fun initSomeThing(): String = ...
      val lazyString = lazy { initSomeThing() }
      val string = lazyString.value
    
  • 当第一次使用 lazyString时, lazy 闭包会调用,它一般可用在单例模式当中。

  • lazy 只用于常量 val

4、lateinit 延迟初始化

  • 在使用lateinit时默认你已经做了初始化操作了,kotlin不会再为你做判空处理,需要你个人确保lateinit标识的对象在使用前已被正确初始化

  • 再也不用在使用代码前判断对象是否为空了(写过Java代码的同学估计都很厌烦):Kotlin引入了非空 ! 和可为空?两种类型来提供安全监测机制,合理使用该机制可以写出很优雅的代码

  • lateinit修饰符允许声明非空类型,并在对象创建后(构造函数调用后)初始化。不使用lateinit则需要声明可空类型并且要有额外的空安全检查操作。

      var string: String? = null
      string?.length
    
      lateinit var lazyString: String
    
      ...
    
      lazyString = "init"
    

注意事项:

  • 使用该类型需谨慎,个人建议能不使用该类型则不使用,原因:使用了该类型时,后续对lateinit的对象进行判空操作时默认均不为null,

        lateinit var test: String
        if (test != null) {
           //该处代码将一直被执行,应为 test != null 永远为 true
       }
    
  • lateinit 只用于变量 var

  • lazy 应用于单例模式,而且当且仅当变量被第一次调用的时候,委托方法才会执行

  • lateinit 则用于只能生命周期流程中进行获取或者初始化的变量

  • lateinit修饰的变量/属性不能是 原始数据类型(Int、Char、Long、Float、Double、Short、Byte)

类型转换

  • 可能换引发崩溃的类型转换:Kotlin中类型转换的关键字是as,如果实际类型不能转换为期望的类型就会引起异常崩溃,即as左侧的对象不能转换为as右侧类型的对象时将引起异常崩溃;

  • 安全的类型转换:使用as?实现安全类型转换,如果转换失败就会返回null。该种方式得到的结果类型也是?的,即可为空的;

      //unsafe
      var c = A as B  //c: B!
      //safe
      var c = A as? B  //c: B?
    

如何安全的接收网络请求数据

  • 在编写bean类进行接收数据时,如果字段不能百分百确定会有,建议都使用?可空类型标识;

扩展函数

  • 扩展函数是指在一个类上增加一种新的行为,甚至我们没有这个类代码的访问权限。在Java中,通常会实现很多带有static方法的工具类,而Kotlin中扩展函数的一个优势是我们不需要在调用方法的时候把整个对象当作参数传入,它表现得就像是属于这个类的一样,而且我们可以使用this关键字和调用所有public方法。

函数扩展

  • 简单来说,Kotlin扩展函数允许我们在不改变已有类的情况下,为类添加新的函数。例如,我们能够为Activity中添加新的方法,让我们以更简单术语显示toast,并且这个函数不需要传入任何context,它可以被任何Context或者它的子类调用,比如Activity或者Service:苹果的新语言swift中通用拥有该特性

  • 为Context类扩展一个好用的toast方法,弹Toast爽的不要不要的

  • 通过以下扩展,Context类型及其子类对象实例便可直接使用toast方法来弹出toast //声明扩展函数 fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) { Toast.makeText(this, message, duration).show() }

      //在Activity或者fragment或者Application类内可直接使用它
      toast("Hello")
      toast("Hello", Toast.LENGTH_SHORT)
      toast(message = "Hello", length = Toast.LENGTH_SHORT)
    
  • 类似的这种我们还可以把LayoutInflater.from(context).inflate赋予ViewGroup;将图片加载框架赋予ImageView;将时间格式化转换赋予Long,扩展View的dp转换函数等等……

  • 有一点值得注意:扩展函数并不是真正地修改了原来的类,它的这些作用效果是以静态导入的方式来实现的。扩展函数可以被声明在任何文件中,因此有个通用的方式是把一系列有关的函数放在一个工具类当中。

属性扩展

  • 扩展函数也可以是一个属性,所以我们可以通过相似的方法来扩展属性。

  • 例如我们可以给View声明一个quickPaddingLeft扩展属性,那么就不需要每次设置四个值了。

      var View.quickPaddingLeft: Int
          set(value) {
              setPadding(value, paddingTop, paddingRight, paddingBottom)
          }
    
          get() {
              return paddingLeft
          }
    

操作符扩展

  • 我们可以使用扩展函数扩展我们已经存在的类来让第三方的库能提供更多的操作。

  • 比如我们可以去像访问List的方式去访问 ViewGroup 的 view:

      operator fun ViewGroup.get(position: Int): View =     getChildAt(position)
    
  • 于是我们就可以像这样非常简单地从一个 ViewGroup 中通过 position 得到一个 view:

      val container: ViewGroup = findViewById(R.id.container)
      val view = container[2]
    

扩展函数的优点:

  • 函数扩展功能使得我们可以为现有的类添加新的函数,实现某一具体功能 。
  • 属性扩展允许定义在类或者kotlin文件中,不允许定义在函数中。
  • 扩展函数是静态解析的,并未对原类添加函数或属性,对类本身没有任何影响。 -扩展函数可以有效的减少代码量,让代码更加整洁,纯粹

Kotlin 标准库常用扩展函数集合

apply

  • apply 是 Any 的扩展函数,因而所有类型都能调用。

  • apply 接受一个lambda表达式作为参数,并在apply调用时立即执行,apply返回原来的对象。

  • apply 主要作用是将多个初始化代码链式操作,提高代码可读性。 val task = Runnable { println(“do something amazing”) } val thread = Thread(task) thread.setDaemon(true) thread.start()

  • 上面这段代码可以简化为:

      val task = Runnable { println("do something amazing") }
      Thread(task).apply { setDaemon(true) }.start()
    

let

  • let 和 apply 类似, 唯一的不同是返回值,let返回的不是原来的对象,而是闭包里面的值,参数为it val dog = Dog() print(dog.let { val ears = “catbro” val eyes = it.eyes ears })

  • 这段代码的返回值就是“catbro”

with

  • with 是一个顶级函数, 当你想调用对象的多个方法但是不想重复对象引用,比如代码:

      val dog: Dog = Dog()
      dog.ears = "dog1"
      dog.eyes = "dog2"
      ...
    
  • 可以用 with 这样写:

      with(dog) {
          ears = "dog1"
          eyes = "dog2"
          ...
      }
    

run

  • run 是 with和let 的组合,例如:

      val dog = Dog()
      print(dog.run {
          legs = "1"
          ears = "2"
          eyes = it.eyes
          legs
      })
      打印结果为1