Kotlin 初学者枚举类-密封类-数据类-继承
Posted 帅次
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 初学者枚举类-密封类-数据类-继承相关的知识,希望对你有一定的参考价值。
作者简介:CSDN博客专家、华为云享专家认证
系列专栏:Kotlin 初学者
学习交流:三人行必有我师焉;择其善者而从之,其不善者而改之。
目录
3.2 toString、equals和hashCode的个性化实现
一、枚举类
枚举类,用来定义常量集合的一种特殊类。使用 enum class 可以声明一个枚举类。
1.1 创建枚举类
枚举常量用逗号分隔,每个枚举常量都是一个对象
enum class Color
RED,
BULE,
YELLOW
fun main()
//使用
println(Color.RED)
1.2 获取枚举相关信息
val name: String //获取枚举名称,,默认名称为枚举字符名
val ordinal: Int //获取枚举值在所有枚举数组中定义的顺序,值从 0 开始
fun main()
//使用
val blue = Color.BULE
println(blue.name)
println(blue.ordinal)
1.3 枚举类添加属性
每一个枚举都是枚举类的实例,它们可以被初始化。
若需要指定值,则可以使用其构造函数。
//2.0版本
enum class Color2(val user: UserColor)
RED(UserColor("小红", 15)),
BULE(UserColor("小蓝", 20)),
YELLOW(UserColor("小黄", 30));
data class UserColor(val name: String, val age: Int)
fun main()
//2.0版本
println(Color2.BULE.user)
1.4 定义函数
enum class Color2(val user: UserColor)
...
RED(UserColor("小红", 15));
fun addUser(adduser:UserColor)=
UserColor("$adduser.name-$user.name"
,adduser.age+user.age)
fun main()
//枚举定义函数
println(Color2.RED.addUser(UserColor("新", 40)))
1.5 以泛型的方式访问枚举类中的常量
自 Kotlin 1.1 起,可以使用 enumValues<T>()
和enumValueOf<T>()
函数以泛型的方式访问枚举类中的常量
@SinceKotlin("1.1")
public inline fun <reified T : Enum<T>> enumValues(): Array<T>
/**
* Returns an enum entry with specified name.
*/
@SinceKotlin("1.1")
public inline fun <reified T : Enum<T>> enumValueOf(name: String): T
//使用
fun main()
println(enumValueOf<Color>("RED"))
println(enumValues<Color2>().joinToString it.user.toString() )
1.6 代数数据类型(ADT)
可以用来表示一组子类型的闭集,枚举类就是一种简单的ADT。
使用枚举的关键好处在于使用 when 表达式的时候,如果能够 验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了。
enum class ColorADT
RED,
BULE,
YELLOW
class TakeColor(var colorADT: ColorADT)
fun selectColor(): String
return when (colorADT)
ColorADT.RED -> "红色"
ColorADT.BULE -> "蓝色"
ColorADT.YELLOW -> "黄色"
// 不再需要 `else` 子句,因为我们已经覆盖了所有的情况
fun main()
println(TakeColor(ColorADT.BULE).selectColor())
但是如果我们想在返回是红色的时候给一个提示。如果使用枚举类,那么还需要判断就有一些复杂了。对于更复杂的ADT,你可以使用Kotlin的密封类(sealed class)来实现更复杂的定义。
二、密封类(sealed class)
-
对于更复杂的ADT,你可以使用Kotlin的密封类(sealed class)来实现更复杂的定义,密封类可以用来定义一个类似于枚举类的ADT,但你可以更灵活地控制某个子类型。
-
密封类可以有若干个子类,要继承密封类,这些子类必须和它定义在同一个文件里。
-
sealed 关键字不能修饰 interface ,abstract class(会报 warning,但是不会出现编译错误)
2.1 创建密封类
sealed class ColorSealed
//以下都是密封类的子类
//使用object用的是单例,因为下面两个子类没有属性,不管生成多少次都一样
object Blue : ColorSealed()
object Yellow : ColorSealed()
//这个子类有属性,可能属性不同,所以要生成不同的对象
class Red(val toast:String) : ColorSealed()
2.2 使用
使用密封类的关键好处在于使用 when 表达式的时候,如果能够 验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了。
class TakeColorSealed(var colorSealed: ColorSealed)
fun selectColor(): String
return when (colorSealed)
is ColorSealed.Blue -> "蓝色"
is ColorSealed.Yellow -> "黄色"
is ColorSealed.Red ->
"红色,$(this.colorSealed as ColorSealed.Red).toast"
//不再需要 else 子句,因为我们已经覆盖了所有的情况
fun main()
println(TakeColorSealed(ColorSealed.Blue).selectColor())
println("----------")
println(TakeColorSealed(ColorSealed.Red("警告警告!!!")).selectColor())
这里跟枚举类的使用不太一样。用到了 is 关键字。
调用红色提示的时候将当前的ColorSealed转换为ColorSealed.Red,因为编译时也不知道你传入的是Red还是其他的。
三、数据类
使用 data class 关键字创建一个只包含数据的类,这种类又称数据类。这个是Kotlin独有的,Java没有数据类。
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
-
生成 equals() 函数与 hasCode() 函数
-
生成 toString() 函数,由类名(参数1 = 值1,参数2 = 值2,….) 构成
-
由所定义的属性自动生成component1()、component2()、…、componentN()函数,其对应于属性的声明顺序。
-
copy() 函数
数据类需要满足以下条件:
-
主构造函数至少包含一个参数。
-
所有的主构造函数的参数必须标识为val或者var。
-
数据类不可以声明为abstract,open,sealed或者inner。
-
数据类不能继承其他类 (但是可以实现接口)。
data class DaTang (var name:String ,val age:Int)
在没有结构体的时候,大括号可省略。
3.1 创建数据类
data class DaTang (var name:String ,val age:Int)
val emperor = "$name,是继隋朝之后的大一统中原王朝,共历二十一帝,享国-$age-年。"
fun main()
println(DaTang("唐朝", 289))
如果不使用 data class 关键字修饰,而使用class关键字修饰,如下:
3.2 toString、equals和hashCode的个性化实现
3.3 ==符号
data class DaTang (var name:String ,val age:Int)
val emperor = "$name,是继隋朝之后的大一统中原王朝,共历二十一帝,享国-$age-年。"
class DaTang2 (var name:String ,val age:Int)
val emperor = "$name,是继隋朝之后的大一统中原王朝,共历二十一帝,享国-$age-年。"
fun main()
println(DaTang("唐朝", 289))
// "==“比较的是内容 equals(Any)。
// 因未重写Any的equals函数,使用的是Any默认equals函数,所以比较的还是引用。
// "===" 比较的是引用(类所占的内存区域)
println(DaTang2("唐朝", 289) == DaTang2("唐朝", 289))
//这里使用的是data class,数据类重写了equals,比较的是数据类里面的数据。
println(DaTang("唐朝", 289) == DaTang("唐朝", 289))
3.4 copy() 函数
copy() 函数应该是类似Java中的clone() 方法
data class DaTang (var name:String ,val age:Int)
val emperor = "$name,是继隋朝之后的大一统中原王朝,共历二十一帝,享国-$age-年。"
fun main()
val datang = DaTang("唐朝", 289)
println(datang)//DaTang(name=唐朝, age=289)
//创建了了一个新的对象
val diguo = datang.copy(age=500)
println(diguo)//DaTang(name=唐朝, age=500)
3.5 解构声明
数据类:由所定义的属性自动生成component1()、component2()、…、componentN()函数,其对应于属性的声明顺序。
普通类:使用 operator 关键字定义component1()、component2()、…、componentN()函数。
data class DataDaSong(var name:String ,var age:Int)
class DaSong(var name:String ,var age:Int)
//解构语法:必须从component1开始
operator fun component1() = name
operator fun component2() = age
fun main()
//使用普通类需要自己写component1、component2...componentN
var (name,age) = DaSong("北宋",167)
println("$name,是中国历史上继五代十国之后的朝代,传九位皇帝,享国-$age-年")
//使用数据类支持解构语法,自动生成operator fun component1
var (dataname,dataage) = DataDaSong("北宋",167)
println("数据类:$dataname,是中国历史上继五代十国之后的朝代,传九位皇帝,享国-$dataage-年")
查看数据类反编译代码:
四、 继承(extend)
Kotlin 允许一个类继承自另一个类,Kotlin 中所有类都继承自 Any 类,Any 类是所有类的超类,对于没有超类型声明的类是默认超类
Kotlin 类默认都是封闭的,要让某个类开放继承,必须使用 open 关键字修饰它。
注意:
在 Kotlin 中 Any 类是所有类的超类
在 Java 中 Object 类是所有类的超类
4.1 Any 超类
Any 默认提供了三个函数:
public open class Any
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
从这里发现 无论是类还是函数,都使用了 open 关键字修饰。
同时发现,这里只是定义了函数,没有实现。为什么呢?因为Kotlin是跨平台语言,可以在android、macOS、Windows、javascript上运行,为了支持跨平台更友好,在不同平台有不同实现,所以在这里并未展示。
4.2 继承类
/* 食物基类 */
open class Food
fun explain() = "Food explain"
class Apple :Food()
不加 open 关键字修饰是不让继承滴,如下图:
4.3 函数重写
在基类中,使用 fun 声明函数时,此函数默认为 final 修饰,不能被子类重写。
如果允许子类重写该函数,那么必须使用 open 关键字修饰它, 子类重写函数使用 override 关键字。
/* 食物基类 */
open class Food
//函数必须用 open 关键字修饰,子类才能覆盖
open fun explain() = "Food explain"
/* 继承Food */
class Apple :Food()
//使用 override 关键字覆盖父类函数
override fun explain() = "Apple explain "
在 Kotlin 中 override 是个关键字而不是注解。
使用
fun main()
//多态:父类类型的引用子类类型的对象
val f: Food = Apple()
println(f.explain())
4.4 属性重写
/* 食物基类 */
open class Food
open val price = 100
...
/* 继承Food */
class Apple : Food()
override var price = 36
...
fun main()
//多态:父类类型的引用子类类型的对象
val f: Food = Apple()
println(f.explain())//Apple explain 36
4.5 类型检测(is
)
Kotlin的 is运算符 可以用来检查某个对象的类型。
println(f is Food)//true
println(f is Apple)//true
println(f is File)//false
智能类型转换(as
)
Kotlin的 as运算符 可以用对某个对象进行类型转换。
智能安全转换操作符:as?
as?安全地转换成一种类型。 如果无法进行转换,则返回null,而不是抛出ClassCastException异常。
咱就在上面的实例基础上修改。
var asTest :String? = ""
//不安全的转换操作符 as
// println(asTest as Int)//ClassCastException
//安全的转换操作符 as?
println(asTest as? Int)//null
以上是关于Kotlin 初学者枚举类-密封类-数据类-继承的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin基础(十四) 枚举类(enum)数据类(data)和密封类(sealed)与java的写法差异
Kotlin常用的 Kotlin 类 ② ( 枚举类 | 枚举类定义函数 | 密封类 )