Kotlin学习笔记12——数据类和密封类
Kotlin学习笔记12——数据类和密封类
- 前言
- 数据类
- 在类体中声明的属性
- 复制
- componentN
- 解构声明
- 密封类
- 尾巴
前言
上一篇,我们学习了Kotlin中的拓展,今天继续来学习Kotlin中的数据类和密封类。
数据类
我们经常创建一些只保存数据的类。 在这些类中,一些标准函数往往是从数据机械推导而来的。在 Kotlin 中,这叫做 数据类 并标记为 data:
data class User(val name: String, val age: Int)
为什么要引入数据类?我们先看一个Java POJO(普通Java对象)
class Dog{ private String name; private String sound; private String age; public String getName() { return name; } public void setName(String name) { this .name = name; } public String getSound() { return sound; } public void setSound(String sound) { this .sound = sound; } public String getAge() { return age; } public void setAge(String age) { this .age = age; }
所有这些仪式都用于一个简单的Java POJO类,该类用于保存一些数据。 在对来自API的JSON响应进行序列化/反序列化时,您可能使用过此类。 它为您提供属性的获取器和设置器。
现在,让我们看看如何借助数据类在Kotlin中完成此操作:
data class Dog(var name: String, var age: String, var sound: String)
没错,只需一行! 我们可以像使用Java类一样继续使用此类。 获取器和设置器是为我们生成的编译器。编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
- equals() / hashCode()
- toString() 格式如 “Dog(name=John, age=42,sound=喵喵)”
- componentN() functions 对应于属性,按声明顺序排列
- copy() 函数
如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。
为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:
- 主构造函数至少包含一个参数。
- 所有的主构造函数的参数必须标识为val 或者 var ;
- 据类不可以声明为 abstract, open, sealed 或者 inner;
- 数据类不能继承其他类 (但是可以实现接口)。
在类体中声明的属性
请注意,对于那些自动生成的函数,编译器只使用在主构造函数内部定义的属性。如需在生成的实现中排除一个属性,请将其声明在类体中:
data class Person(val name: String) {var age: Int = 0
}
在 toString()、 equals()、 hashCode() 以及 copy() 的实现中只会用到 name 属性,并且只有一个 component 函数 component1(),且这个component1返回的是name的值。
复制
复制使用 copy() 函数,我们可以使用该函数复制对象并修改部分属性, 其实现会类似下面这样:
data class Cat(var name: String, var age: String, var sound: String)var cat = Cat("Jim","2","喵喵")
var catTom = cat.copy(name="Tom")
println("catTom'name is ${catTom.name}")
//打印结果:catTom'name is Tom
componentN
使用数据类对象调用componentN函数可以按声明顺序获取数据类中的属性值,但不包括再类体中声明的属性。
data class Cat(var name: String, var age: String, var sound: String){//类体中声明属性var height:Int = 30
}var cat = Cat("Jim","2","喵喵")
var catTom = cat.copy(name="Tom")
println("catTom'name is ${catTom.name} component1 : ${cat.component1()} component2 : ${cat.component2()} component3 : ${cat.component3()}")
//打印结果:catTom'name is Tom component1 : Jim component2 : 2 component3 : 喵喵
解构声明
组件函数允许数据类在解构声明中使用:
var cat = Cat("Jim","2","喵喵")
val (name,age,sound) = cat
println("$name $age $sound")
//打印结果:Jim 2 喵喵
有时把一个对象 解构 成很多变量会很方便:
val (name,age,sound) = cat
这种语法称为 解构声明 。一个解构声明同时创建多个变量。 我们已经声明了两个新变量: name 和 age,sound并且可以独立使用它们:
...
println("$name $age $sound")
...
以上等价于:
...
println("${cat.component1()} ${cat.component2()} ${cat.component3()}")
...
密封类
密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。
声明一个密封类,使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。sealed 不能修饰 interface ,abstract class(会报 warning,但是不会出现编译错误)
sealed class Expr {data class Const(val number: Double) : Expr()data class Sum(val e1: Expr, val e2: Expr) : Expr()object NotANumber : Expr()
}fun eval(expr: Expr): Double = when(expr) {is Expr.Const -> expr.numberis Expr.Sum -> eval(expr.e1) + eval(expr.e2)Expr.NotANumber -> Double.NaN// 不再需要 `else` 子句,因为我们已经覆盖了所有的情况
}
密封类就是一种专门用来配合 when 语句使用的类。
举个例子,假如在 Android 中我们有一个 view,我们现在想通过 when 语句设置针对 view 进行两种操作:显示和隐藏,那么就可以这样做:
sealed class UiOp {object Show: UiOp()object Hide: UiOp()
}
fun execute(view: View, op: UiOp) = when (op) {UiOp.Show -> view.visibility = View.VISIBLEUiOp.Hide -> view.visibility = View.GONE
}
以上功能其实完全可以用枚举实现,但是如果我们现在想加两个操作:水平平移和纵向平移,并且还要携带一些数据,比如平移了多少距离,平移过程的动画类型等数据,用枚举显然就不太好办了,这时密封类的优势就可以发挥了,例如:
sealed class UiOp {object Show: UiOp()object Hide: UiOp()class TranslateX(val px: Float): UiOp()class TranslateY(val px: Float): UiOp()
}fun execute(view: View, op: UiOp) = when (op) {UiOp.Show -> view.visibility = View.VISIBLEUiOp.Hide -> view.visibility = View.GONEis UiOp.TranslateX -> view.translationX = op.px // 这个 when 语句分支不仅告诉 view 要水平移动,还告诉 view 需要移动多少距离,这是枚举等 Java 传统思想不容易实现的is UiOp.TranslateY -> view.translationY = op.px
}
以上代码中,TranslateX 是一个类,它可以携带多于一个的信息,比如除了告诉 view 需要水平平移之外,还可以告诉 view 平移多少像素,甚至还可以告诉 view 平移的动画类型等信息,我想这大概就是密封类出现的意义吧。
除此之外,如果 when 语句的分支不需要携带除“显示或隐藏view之外的其它信息”时(即只需要表明 when 语句分支,不需要携带额外数据时),用 object 关键字创建单例就可以了,并且此时 when 子句不需要使用 is 关键字。只有需要携带额外信息时才定义密封类的子类,而且使用了密封类就不需要使用 else 子句,每当我们多增加一个密封类的子类或单例,编译器就会在 when 语句中给出提示,可以在编译阶段就及时发现错误,这也是以往 switch-case 语句和枚举不具备的功能。
最后,我们甚至可以把这一组操作封装成一个函数,以便日后调用,如下:
// 先封装一个UI操作列表
class Ui(val uiOps: List = emptyList()) {//这里是用operator关键字对+进行重载operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)
}// 定义一组操作
val ui = Ui() +UiOp.Show +UiOp.TranslateX(20f) +UiOp.TranslateY(40f) +UiOp.Hide
// 定义调用的函数
fun run(view: View, ui: Ui) {ui.uiOps.forEach { execute(view, it) }
}run(view, ui) // 最终调用
尾巴
今天的学习笔记就先到这里了,下一篇我们将学习Kotlin中的泛型。
老规矩,喜欢我的文章,欢迎素质三连:点赞,评论,关注,谢谢大家!
Kotlin学习笔记12——数据类和密封类相关推荐
- Kotlin学习笔记 第二章 类与对象 第十一节 枚举类 第八节密封类
参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...
- Kotlin学习笔记 第二章 类与对象 第七节 数据类
参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...
- Kotlin学习笔记 第二章 类与对象 第三节接口 第四节 函数式接口
参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...
- Kotlin学习笔记 第二章 类与对象 第一节类与继承(补)
参考链接 Kotlin官方文档 Kotlin docs | Kotlin 本系列为参考Kotlin中文文档 kotlin官方文档2020版.pdf-其它文档类资源-CSDN下载 第二章 第一节 类与继 ...
- Kotlin学习笔记 第二章 类与对象 第十四 十五节 委托 委托属性
参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...
- Kotlin学习笔记 第二章 类与对象 第十二 十三节 对象表达式与对象声明 类型别名
参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...
- Kotlin学习笔记 第二章 类与对象 第二节属性
参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...
- Kotlin学习笔记 第二章 类与对象 第九节 泛型
参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...
- Kotlin学习笔记 第二章 类与对象 第十节 内部类嵌套类
参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...
最新文章
- 腾讯/字节/华为/旷视 2022届实习面经—计算机视觉方向
- Highcharts X轴纵向显示
- ITK:拉普拉斯递归高斯图像滤波器
- Python将文字转拼音
- 为什么年龄大了近视还增加_年龄明明一样大,为什么有人长得年轻,有人显老呢?...
- java自定义equals函数和hashCode函数
- Hierarchical voxel block hashing for effiecient integration of depth images
- 数源思维完成目标设定
- RedHat 网络配置
- MT4跟单软件多帐户跨平台如何解决喊单账号与跟单账号个别品种合约数量不一致的问题?——Hookswork
- 公众号开发定时群发php,微信公众平台新增定时群发功能(新功能)
- python 董付国程序设计基础与应用第三版pdf_Python 程序设计基础(董付国 著)完整版PDF[6MB]...
- Hive 正则表达式 判断字符串是否是数字(整数)
- 惠普gk100好不好_「商家透露」惠普gk100和gk400区别比较 哪款好?这样选不盲目...
- charles抓包https设置
- ctf新手总结--web做题
- 贷款需要查询个人征信报告?-民兴商学院
- 河北师范大学的计算机专业好吗,河北师范大学计算机应用技术到底咋样啊?
- 22岁失去 “万亿美金机会”,28岁被硅谷最牛孵化器录取,30岁他的经历被拍成电视剧...
- 博奥智源公司,图书馆管理软件开发功能详解