这次更新文章间隔有点久……没办法,农村娃儿,在加上每天的网课……不过,幸好是要开学了。
喜大普奔。
也刚好乘着今天这个特殊的日子:
祝自己的妈妈,以及天下所有的妈妈!母亲节快乐!
emmm……
前面我们说了类与对象,类是对象的模板,对象是类的实现。
然,kotlin完美吸收了java的面向对象思想,并且!还添加了一系列诸如数据类
、密封类
等新的特性。
数据类
java编程中,我们通常会封装一个JavaBean来实现各种数据,在kotlin中则是将这种封装单独了出来,专门用一种以data
关键字作为定义而生成的类,我们首先来看看数据类的定义。
data class UserBean(val name:String, val pass:String){……}
按照官网文档所说:
- 主构造函数需要至少有一个参数;
- 主构造函数的所有参数需要标记为
val
或var
; - 数据类不能是抽象、开放、密封或者内部的;
- (在1.1之前)数据类只能实现接口。
意思很明确了吧,
此外,数据类不能被继承!
但是数据类又继承自Any
(也就是java中的Object
)
所以,它存在以下方法:
- equals() / hashCode()
- toString()
- componentN()
- copy()
接下来,我们来看看这些方法!
以上是继承自Any
的基本方法,另外我们再来看两个加粗的方法。
先说copy()
,复制。
上图很清晰明了,copy() 的产生理由:某些时候需要改变对象的<font color=red>部分属性</font>但又要保持其自身的其他属性不变。
源代码很简单:
@NotNull
public final UserBean copy(@NotNull String name, @NotNull String pass) {
Intrinsics.checkParameterIsNotNull(name, "name");
Intrinsics.checkParameterIsNotNull(pass, "pass");
return new UserBean(name, pass);
}
然后就是这里的Intrinsics.checkParameterIsNotNull
方法。
public static void checkParameterIsNotNull(Object value, String paramName) {
if (value == null) {
throwParameterIsNullException(paramName);
}
}
很简单吧,就是一个判空函数。
解构函数
然后,就是另一个加粗的componentN()
了,注意了,这里的N并不是字母,而是number
数字。
首先来举个栗子。
看到了吧,就是这样!
嘿嘿,开玩笑。
fun main(args: Array<String>) {
val user: UserBean = UserBean("张三", "123")
val user1: UserBean = UserBean("李四", "12346578")
……
//componentN()
val (cName, cAge) = user
println(cName)
println(cAge)
}
怎么样?看懂没?
不懂没关系,我也不懂。
这样看明白了吧?
还不明白?
那我们来看看源码
kotlin为每一个数据类成员都声明了一个componetN
,以供解构函数的调用
也就是说
val (name, age) = user
//等同于
val name = user.component1()
val age = user.component2()
在之前循环一节我也提到过
for( (key,vlaue) : map ){
println("key=$key,value=$vlaue")
}
上例也是使用的解构函数,值得注意的是:一旦使用了解构函数!解构列表需要与主构造器中的参数列表的,数量、类型,一一对应。
密封类
密封类?蜜蜂类?好吧……皮一下,不要在意这些无关紧要的细节。
kotlin中的密封类与枚举有些类似,它是定义了受限制的类继承结构。
字面意思来理解就是……
类中的类型只能是有限的几种类型,不能是其他的类型
按照枚举的说法就是,只能是枚举类的东西,其他东西枚举类匹配不到
避免混淆,我先说明一下:
- 密封类是允许被继承的,但是!密封类的继承只能在同一个文件之中进行(kt1.1版本之前还只能允许嵌套在其内部……)
- 密封类不能被直接实例化,它是一个隐藏的抽象类的
- 密封类只允许有
private
的构造函数!
密封类的定义关键字:sealed
来看代码
//定义密封类(空类)
sealed class SealedExpr
//继承
data class Car(val name: String, val type: String) : SealedExpr()
data class Food(val fName: String, val fType: String) : SealedExpr()
//run
fun main() {
val car: SealedExpr = Car("大众", "白色");
val food: SealedExpr = Car("西红柿", "红色");
println(test(car))
println(test(food))
}
fun test(p: SealedExpr): String = when (p) {
is Car -> p.cName
is Food -> p.fName
}
在内部定义
sealed class SealedExpr1{
data class Car1(val cName: String, val cType: String) : SealedExpr1()
data class Food1(val fName: String, val fType: String) : SealedExpr1()
}
//run
fun main() {
val car: SealedExpr1 = SealedExpr1.Car1("大众", "白色");
val food: SealedExpr1 = SealedExpr1.Car1("西红柿", "红色");
println(test(car))
println(test(food))
}
fun test(p: SealedExpr1): String = when (p) {
is SealedExpr1.Car1 -> p.cName
is SealedExpr1.Food1 -> p.fName
}
这就完了,但是为什么上面要说它与枚举类类似呢?
这就来说说:
- 就那
when
表达式来说,一旦when
表达式中使用了超类,如上例中的:SealedExpr
那么只要是继承自SealedExpr
的子类必须要全部写,否则你只能写else
,枚举也是一样的。
- 但是密封类与枚举类又不一样,枚举类只能存在一个实例,然而密封类可以存在多个实例。
总结
- 本来想把泛型也一起写了,但是……再写下来好像有点多了,下回分解吧。
- kotlin中存在数据类(data)与密封类(saled)。
- 数据类无法被继承,不能拥有子类,必须拥有至少带一个参数的构造方法,kotlin为每一个密封类成员都提供了一个
componetN
以便于解构函数调用。 - 密封类允许被继承,但是!只能在同一个文件或者该密封内内部继承,且密封类的构造函数只能是
private
。