Kotlin 数据类、解构声明与单例模式
在之前的三篇文章中,我们已经了解了一下的内容:
- 如何在项目中使用 Kotlin,Kotlin 的空安全
- 集合的相关操作,扩展函数以及 Lambda 表达式等高级特性
- for 与集合遍历,强大的 if 、when 表达式,可以用来做单例模式的伴生对象
1、数据类
你是否已经厌烦了一个项目中大量的 POJO ?是的,虽然我们有大量的插件来帮助我们简化这些创建过程,但是满天的 getter
与 setter
还有那些 equals()/hashCode()
大量机械的、重复的方法,早已让我们厌烦不堪。
Kotlin 为我们提供了更强大数据类,如下所示:
1 | data class User(var name: String="", var age:Int = 0){ |
使用 data 关键字来告知系统这是一个数据类,其主构造函数需要至少有一个参数,如果生成的类需要含有一个无参的构造函数,则所有的属性必须指定默认值。
请注意,对于那些自动生成的函数,编译器只使用在主构造函数内部定义的属性。如需在生成的实现中排除一个属性,请将其声明在类体中。
1.1、复制一个数据类
当我们使用 copy
函数时,系统会找到该类的主构造函数的签名,此时我们可以按顺序填入参数(无需完全填写,会从第一位开始自动匹配),还可以采用类似 Python 的方式 参数名 = 值
的方式来修改我们需要的参数值。
2、解构声明
这四个字对于 Android + Java 开发者是挺陌生的,大家可以点击到 Kotlin 中文站查看详细的定义,下面我们通过代码来了解一下他的用法,首先我们来看一张熟悉的图:
注意这里的 (k,v)
这就是一个解构声明,标准库已经帮我们实现了解构声明,另外数据类也已经实现了主构造函数包含参数的解构声明。
比如刚刚我们创建的 User 类,我们可以通过如下的方式来获取其中指定的值:
1 | var (name,age) = c |
我们可以任意的在类中进行解构声明,比如我们可以使用解构声明来扩展刚刚我们定义的 User 类:
1 | data class User(var name: String="", var age:Int = 0){ |
需要注意的是,数据类默认为我们实现了主构造函数的参数解构声明,所以我们的解构声明只能接着主构造函数的序号往后使用
2.1、在 Lambda 表达式中使用解构声明
当这个类进行了解构声明时,我们可以直接在 Lambda 表达式中使用其解构,如下:
1 | c.let { (a,b,c) -> |
请注意解构声明对于 Lambda 表达式而言是一个参数:
1 | { a //-> …… } // 一个参数 |
在函数中返回数个参数
有的时候我们经常会希望一个函数能返给我们多个结果,例如曾经我做过一个小功能:传入 PM2.5 的值,返回当前 PM2.5 严重程度的介绍以及需要显示的颜色代码。当时我采用的做法是返回一个 Map,然后再需要的地方在使用固定的 key 将值提取出来,这一点也不优雅。
但是在 Kotlin 里 你可以这样做:
1 | fun getPM25DescAndColor(pm25: Int):Pair<String,String> { |
标准库提供了 Pair
和 Triple
这两个类,前者表示两个参数,后者表示三个参数。尽管在很多情况下命名数据类是更好的设计选择, 因为它们通过为属性提供有意义的名称使代码更具可读性。
3、单例模式
不要在考虑单例模式的几种写法了,也不要在关注什么线程安全、线程不安全了。
不知道你有没有疑惑过,Java 是一个面向对象编程的语言(OOP),但是 Java 里有些东西似乎不那么 OOP,比如单例模式,又比如公共静态方法。这两者都必须在一个 class 下面,他们却都不应该是一个类。一个单例模式,既然是一个 class 为什么不能处处实例化它?一个个公共静态方法,调用它为什么还要先写一个 class?这并不符合直觉!
Kotlin 帮我们彻底的解决了这一迷思!本篇我们先来说说在 Kotlin 里如何写好一个单例模式。
在 Java 里我最常用的单例模式是静态内部类,因为他即是线程安全又是懒加载的,我们来看一下一个最简单的静态内部类单例模式:
1 | public class Singleton { |
我们通过私有的构造函数来使得外部类不能创建这个类的实例,在私有的内部静态类中实例化这个类,然后提供一个公共静态访问内部类中的成员。
我们来看看这样一个单例我们在 Kotlin 中应该如何处理:
1 | object Singleton { |
是的就是这么简单,我们只要使用 object
关键字就可以声明一个单例,这是真正意义上的单例,因为他本身就是一个对象!在 Kotlin 中这被称作对象声明,就像变量声明一样,对象声明不是一个表达式,不能用在赋值语句的右边。
对象声明的初始化过程是线程安全的。
如需引用该对象,我们直接使用其名称即可:
1 | Singleton.doSomething() |
如果你只是在某个 kt 文件中需要使用一个单例模式的对象,我们还可以使用 object 关键字来实现 Java 中的匿名内部类:
1 | var e = object { |
当然这个对象还可存在继承与实现:
1 | open class A(x: Int) { |
PS:诚如本篇文章的标题,简单就是美,Kotlin 中还有很多这样充满美感的设计或是语法糖,请继续关注后续文章!