Kotlin 初学者枚举类-密封类-数据类-继承

Posted 帅次

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 初学者枚举类-密封类-数据类-继承相关的知识,希望对你有一定的参考价值。

作者简介:CSDN博客专家、华为云享专家认证

系列专栏:Kotlin 初学者

学习交流:三人行必有我师焉;择其善者而从之,其不善者而改之。

目录

一、枚举类

1.1 创建枚举类

1.2 获取枚举相关信息

1.3 枚举类添加属性

1.4 定义函数

1.5 以泛型的方式访问枚举类中的常量

1.6 代数数据类型(ADT)

二、密封类(sealed class)

2.1 创建密封类

2.2 使用

三、数据类

3.1 创建数据类

3.2 toString、equals和hashCode的个性化实现

3.3 ==符号

3.4 copy() 函数

3.5 解构声明

四、 继承(extend)

4.1 Any 超类

4.2 继承类

4.3 函数重写

4.4 属性重写

4.5 类型检测(is)


一、枚举类

        枚举类,用来定义常量集合的一种特殊类。使用 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 类 ② ( 枚举类 | 枚举类定义函数 | 密封类 )

Kotlin常用的 Kotlin 类 ② ( 枚举类 | 枚举类定义函数 | 密封类 )

Kotlin学习手记——单例内部类数据类枚举类密封类内联类

Kotlin 代数/枚举/密封类

kotlin sealed密封类