[4] —— Kotlin 数据类、解构声明与单例模式-简单即是美

在之前的三篇文章中,我们已经了解了一下的内容:

  1. 如何在项目中使用 Kotlin,Kotlin 的空安全
  2. 集合的相关操作,扩展函数以及 Lambda 表达式等高级特性
  3. for 与集合遍历,强大的 if 、when 表达式,可以用来做单例模式的伴生对象

1、数据类

你是否已经厌烦了一个项目中大量的 POJO ?是的,虽然我们有大量的插件来帮助我们简化这些创建过程,但是满天的 gettersetter 还有那些 equals()/hashCode() 大量机械的、重复的方法,早已让我们厌烦不堪。

Kotlin 为我们提供了更强大数据类,如下所示:

data class User(var name: String="", var age:Int = 0){
    var nickname:String = ""
}

使用 data 关键字来告知系统这是一个数据类,其主构造函数需要至少有一个参数,如果生成的类需要含有一个无参的构造函数,则所有的属性必须指定默认值。

请注意,对于那些自动生成的函数,编译器只使用在主构造函数内部定义的属性。如需在生成的实现中排出一个属性,请将其声明在类体中。

自动生成的函数只是用主构造函数定义的属性

1.1、复制一个数据类

复制数据类的两个方式

当我们使用 copy 函数时,系统会找到该类的主构造函数的签名,此时我们可以按顺序填入参数(无需完全填写,会从第一位开始自动匹配),还可以采用类似 Python 的方式 参数名 = 值的方式来修改我们需要的参数值。

2、解构声明

这四个字对于 Android + Java 开发者是挺陌生的,大家可以点击到 Kotlin 中文站查看详细的定义,下面我们通过代码来了解一下他的用法,首先我们来看一张熟悉的图:


for 循环遍历 Map 集合

注意这里的 (k,v) 这就是一个解构声明,标准库已经帮我们实现了解构声明,另外数据类也已经实现了主构造函数包含参数的解构声明。

比如刚刚我们创建的 User 类,我们可以通过如下的方式来获取其中指定的值:

var (name,age) = c
var (_,aged) = d  //用下划线占位不需要的参数
var aged1 = d.component2()  //直接使用 componentN 函数来获取对应的值
println("c's name = $name c's age = $age")
println("don't need d's name, d's age = $aged")
println("other way to get d's age is $aged1")

我们可以任意的在类中进行解构声明,比如我们可以使用解构声明来扩展刚刚我们定义的 User 类:

data class User(var name: String="", var age:Int = 0){
    var nickname:String = ""

    operator fun component3() = nickname
}

需要注意的是,数据类默认为我们实现了主构造函数的参数解构声明,所以我们的解构声明只能接着主构造函数的序号往后使用

自定义的解构声明

2.1、在 Lambda 表达式中使用解构声明

当这个类进行了解构声明时,我们可以直接在 Lambda 表达式中使用其解构,如下:

c.let { (a,b,c) ->
            println("c's name = $a , c's age = $b , c's nickname = $c")
        }

请注意解构声明对于 Lambda 表达式而言是一个参数:

{ a //-> …… } // 一个参数
{ a, b //-> …… } // 两个参数
{ (a, b) //-> …… } // 一个解构对
{ (a, b), c //-> …… } // 一个解构对以及其他参数

在函数中返回数个参数

有的时候我们经常会希望一个函数能返给我们多个结果,例如曾经我做过一个小功能:传入 PM2.5 的值,返回当前 PM2.5 严重程度的介绍以及需要显示的颜色代码。当时我采用的做法是返回一个 Map,然后再需要的地方在使用固定的 key 将值提取出来,这一点也不优雅。

但是在 Kotlin 里 你可以这样做:

    fun getPM25DescAndColor(pm25: Int):Pair<String,String> {
        return when (pm25) {
            in 0..50 -> Pair("优秀","#009900")
            in 51..100 -> Pair("良好","#FFCC33")
            in 101..150 -> Pair("轻度","#FF9900")
            in 151..200 -> Pair("中度","#FF3300")
            in 201..300 -> Pair("重度","#9900FF")
            else -> Pair("严重","#663300")
        }
    }

标准库提供了 PairTriple 这两个类,前者表示两个参数,后者表示三个参数。尽管在很多情况下命名数据类是更好的设计选择, 因为它们通过为属性提供有意义的名称使代码更具可读性。

3、单例模式

不要在考虑单例模式的几种写法了,也不要在关注什么线程安全、线程不安全了。

不知道你有没有疑惑过,Java 是一个面向对象编程的语言(OOP),但是 Java 里有些东西似乎不那么 OOP,比如单例模式,又比如公共静态方法。这两者都必须在一个 class 下面,他们却都不应该是一个类。一个单例模式,既然是一个 class 为什么不能处处实例化它?一个个公共静态方法,调用它为什么还要先写一个 class?这并不符合直觉!

Kotlin 帮我们彻底的解决了这一迷思!本篇我们先来说说在 Kotlin 里如何写好一个单例模式。

在 Java 里我最常用的单例模式是静态内部类,因为他即是线程安全又是懒加载的,我们来看一下一个最简单的静态内部类单例模式:

public class Singleton {

    //构造方法私有
    private Singleton() {
    }

    //在访问HttpMethods时创建单例
    private static class SingletonHolder{
        private static final Singleton INSTANCE = new Singleton();
    }

    //获取单例
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

我们通过私有的构造函数来使得外部类不能创建这个类的实例,在私有的内部静态类中实例化这个类,然后提供一个公共静态访问内部类中的成员。

我们来看看这样一个单例我们在 Kotlin 中应该如何处理:

object Singleton {
    fun doSomething(){
    }
}

是的就是这么简单,我们只要使用 object 关键字就可以声明一个单例,这是真正意义上的单例,因为他本身就是一个对象!在 Kotlin 中这被称作对象声明,就像变量声明一样,对象声明不是一个表达式,不能用在赋值语句的右边。

对象声明的初始化过程是线程安全的。

如需引用该对象,我们直接使用其名称即可:

Singleton.doSomething()

如果你只是在某个 kt 文件中需要使用一个单例模式的对象,我们还可以使用 object 关键字来实现 Java 中的匿名内部类:

var e = object {
    fun print(index: Int): String {
        return "你输入的数字是:$index"
    }
}
println(e.print(34))

当然这个对象还可存在继承与实现:

open class A(x: Int) {
    public open val y: Int = x
}

interface B {……}

val ab: A = object : A(1), B {
    override val y = 15
}

PS:诚如本篇文章的标题,简单就是美,Kotlin 中还有很多这样充满美感的设计或是语法糖,请继续关注后续文章!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容