Kotlin学习(12)→几种特殊的类


在期待着郭霖先生的《第一行代码(第三版)》时,意识到自己需要补充必要的Kotlin知识。现在写文章分享自己学习Kotlin基础知识的过程,争取拿到书之后能早日上手。
使用的软件是IDEA 2019,文章中如有错误或者欠缺的地方,欢迎批评指正。
(参考书为《Kotlin从零到精通Android开发》,欧阳燊著,清华大学出版社2018年4月第一版,ISBN 978-7-302-49814-8)。感谢欧阳先生的优秀教材。

Kotlin类的知识快整理完了,最后还要讲几个特殊的Kotlin类,这些特殊的类为开发者提供了极大地方便。

1、嵌套类

Kotlin的类中允许再定义一个类,按照普通的类声明方法即可,这样就能声明出一个嵌套类。
例如(代码来源于参考书):

class Tree(var treename:String) {
    class Flower(var flowerName:String) {
        fun getName():String {
            return "这是一朵$flowerName"
        }
    }
}

如上所示,Flower就是Tree的嵌套类,注意嵌套类不能访问外部的类的成员

要使用嵌套类,需要通过外部的类的类名调用。如下:

class Tree(var treename:String) {
    class Flower(var flowerName:String) {
        fun getName():String {
            return "这是一朵$flowerName"
        }
    }
}

fun main() {
    var result = Tree.Flower("小花").getName()
    println(result)
}

输出结果:

这是一朵小花

2、内部类

内部类可以访问外部类的成员,没有了之前嵌套类的局限性。在class前面加入inner即可表示内部类声明,我们在之前的代码基础上修改:

class Tree(var treename:String) {

    inner class Flower(var flowerName:String) {
        fun getName():String {
        //可以访问外部类成员treename
            return "这是${treename}上的一朵$flowerName"
        }
    }
}

fun main() {
    var result = Tree("桃树").Flower("小花").getName()
    println(result)
}

输出结果:

这是桃树上的一朵小花

可以看到,内部类是需要通过外部类对象来调用的。

3、枚举类

Java和C++都有枚举类型。这些枚举类型可以有效地表达语义。之前学习过的伴生对象,其中定义的属性,也能够当做静态属性使用,同样可以起到明确语义的效果,一般是声明一些常量。
声明一个枚举类非常简单,我们准备好一个类,用enum关键字声明其为枚举类:

enum class Chicken(val chickenName:String) {
    COCK("公鸡"),
    HEN("母鸡"),
    CHICK("小鸡")
}

枚举类内部可以直接写枚举变量,如上面的COCK、HEN、CHICK。可以为Chicken枚举类指定参数列表,实际上,枚举变量是枚举类的实例,因此,每个枚举变量可以调用构造函数进行成员属性的赋值。如COCK对应的公鸡、HEN对应母鸡,CHICK对应小鸡。
枚举变量间要用逗号分开,即便是不在同一行也是如此。
枚举变量自身已经有一些属性,如ordinal属性用于获取该枚举值的序号,name属性用于获取枚举值的名称。
我们再修改一下代码,补上主函数,查看枚举变量的属性:

enum class Chicken(val chickenName:String) {
    COCK("公鸡"),
    HEN("母鸡"),
    CHICK("小鸡")
}

fun main() {
    var chickArray:Array<Chicken> = arrayOf(Chicken.COCK,Chicken.HEN,Chicken.CHICK)
    var i=2
    while(i>=0) {
        println("${chickArray[i].ordinal}号是${chickArray[i].name}")
        i--
    }
}

可见,可以通过枚举类名访问枚举类中的枚举变量。
输出的结果是:

2号是CHICK
1号是HEN
0号是COCK

由此应该清楚了ordinal和name指的是什么了。

4、密封类

密封类的提出可以解决枚举类的多余分支问题。例如用when-else选择枚举类,本来枚举类有四个值,照理说四个条件和选择即可,但是最后一定要有一个else分支,这是多此一举的。但这样做是因为,when不知道枚举类型中有4个枚举值,为了以防万一,必须要有else分支,用来输出一些信息等。
Kotlin提出了密封类的概念,就像是一种更加严格的枚举类。密封类要加关键字sealed声明,内部采用嵌套类的办法,内部的嵌套类全部由自身派生而来。
先给出一个形式上的密封类定义:

sealed class Season {
    class Spring(var name:String) : Season()
    class Summer(var name:String) : Season()
    class Autumn(var name:String) : Season()
    class Winter(var name:String) : Season()
}

四个季节的类,都被装在密封类Season中。这样的话,我们可以不用指定else,输出各个信息:

sealed class Season {
    class Spring(var name:String) : Season()
    class Summer(var name:String) : Season()
    class Autumn(var name:String) : Season()
    class Winter(var name:String) : Season()
}

fun main() {
    var choose:Season = Season.Spring("春天")
    
    when(choose) {
        is Season.Spring->println("是的")
        is Season.Summer->println("不是夏天")
        is Season.Autumn-> println("不是秋天")
        is Season.Winter-> println("不是冬天")
    }
}

这是一个很简单的程序,输出的结果是:

是的

其实我觉得密封类还有很多好处,比如可以更加明确的指定类之间的关系,比起让四季的类继承同一个季节类,我认为有时候把它们密封到一个季节类中会更好。

之后还有数据类和模板类,稍后更新。


5、数据类

程序开发免不了要用到各种数据,一般的,我们会建立一个实体类来存储这些数据,配套会有对数据进行各种操作的函数,如增、删、查、改等等的工具函数。这样的思路是很正常的,但是会增加很多编码量。
Kotlin提出了数据类的概念,一定程度上解放了程序员。在class前加上data,即可将该类声明为数据类。
当然,数据类也有一些要求:

  • 数据类必须要有主构造函数,且至少要有一个输入参数,因为它的属性要和输入参数一一对应,没有属性这个类也就存不了数据了;
  • 主构造函数的参数名前要加val或者var,原因上一点已经说明了
  • 数据类是一个单独的类,有自己的规则,不能是其他类,如子类、基类、密封类、内部类、嵌套类等。

数据类的方便之处在于:

  • 自动声明与构造函数参数同名的属性(因为要带val或者var)
  • 自动实现每个属性的get、set方法
  • 自动提供equal方法,判断两个属性是否相等
  • 自动提供copy方法,用于复制数据,也可以给指定的参数另外复制
  • 提供toString方法,用于打印数据中保存的所有字段值。

下面看代码,准备好一个数据类Animal,写代码可能更加简单易懂。

data class Animal(val Pig:String,val Chick:String,val Dog:String,val Horse:String,val Fox:String) {
    //准备好数据类,创建好五个属性,Pig、Chick、Dog、Horse和Fox,都为String类型
}

fun main() {
    var farm = Animal("小猪","小鸡","小狗","小马","狐狸")
    //复制Farm到变量Zoo,动物都进动物园
    var zoo = farm.copy()

    //猪出栏了,现在新建newZoo,没有猪
    var newZoo = farm.copy(Pig="没有小猪了")

    if(newZoo.equals(zoo)) println("新动物园和老动物园一样") else println("新动物园和老动物园不一样")
    var whatIsInZoo = newZoo.toString() 
    println(whatIsInZoo)
}

输出结果:

新动物园和老动物园不一样
Animal(Pig=没有小猪了, Chick=小鸡, Dog=小狗, Horse=小马, Fox=狐狸)

6、模板类

模板类的思路和泛型函数是差不多的,在类名后面加上尖括号T即可将该类声明为模板类, 一般类中有些属性中用到了这个不确定的类型T,我们就把它声明为模板类。
下面给出一个很简单的模板类,接给出使用模板类创建对象的办法:

class TemplateTest<T>(var message:T?=null) {
    fun printMessage() {
        println(message)
    }
}

fun main() {
    var test = TemplateTest<String>("SSSS.GRIDMAN")
    test.printMessage()
    var test2 = TemplateTest<Int>(2333)
    test2.printMessage()
}

输出结果:

SSSS.GRIDMAN
2333

在声明数组的时候,我们就经常用到模板类的办法,可以回想一下。模板类用得好应该还是非常方便的。。。

至此欧阳先生书上关于Kotlin基础语法的部分就介绍到这里了,实际上Kotlin中还有异常捕获和处理等知识,之后依然会有更新。
再次感谢欧阳先生的精彩教材。用来学习基础语法真的挺不错的。不过,教材中关于Android开发的部分,还是要先有一定的开发基础才能理解的比较好,建议先学一学郭霖先生的教材…

关于基础知识总结完之后,我会进行关于Kotlin开发Android应用程序的各种知识的更新,如果喜欢请继续关注。谢谢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值