1、嵌套类
一个类可以在单独的代码文件中定义,也可以在另一个类内部定义,后一种情况叫做嵌套类,意即A类嵌套在B类之中。乍看过去,这个嵌套类的定义似乎与Java的嵌套类是一样的,但其实有所差别。Java的嵌套类允许访问外部类的成员,而Kotlin的嵌套类不允许访问外部类的成员。倘若Kotlin的嵌套类内部强行访问外部类的成员,则编译器会报错“Unresolved reference: ***”,意思是找不到这个东西。下面是Kotlin定义嵌套类的代码例子:
class Tree(var treeName:String) {
//在类内部再定义一个类,这个新类称作嵌套类
class Flower (var flowerName:String) {
fun getName():String {
return "这是一朵$flowerName"
//普通的嵌套类不能访问外部类的成员如treeName
//否则编译器报错“Unresolved reference: ***”
//return "这是${treeName}上的一朵$flowerName"
}
}
}
2、内部类
既然Kotlin限制了嵌套类不能访问外部类的成员,那还有什么办法可以实现此功能呢?针对该问题,Kotlin另外增加了关键字inner表示内部,把inner加在嵌套类的class前面,于是嵌套类华丽丽转变为了内部类,这个内部类比起嵌套类的好处,便是能够访问外部类的成员。所以,Kotlin的内部类就相当于Java的嵌套类,而Kotlin的嵌套类则是加了访问限制的内部类。按照前面演示嵌套类的树木类Tree,也给它补充内部类的定义,代码如下所示:
class Tree(var treeName:String) {
//在类内部再定义一个类,这个新类称作嵌套类
class Flower (var flowerName:String) {
fun getName():String {
return "这是一朵$flowerName"
//普通的嵌套类不能访问外部类的成员如treeName
//否则编译器报错“Unresolved reference: ***”
//return "这是${treeName}上的一朵$flowerName"
}
}
//嵌套类加上了inner前缀,就成为了内部类
inner class Fruit (var fruitName:String) {
fun getName():String {
//只有声明为内部类(添加了关键字inner),才能访问外部类的成员
return "这是${treeName}长出来的$fruitName"
}
}
}
3、枚举类
Java有一种枚举类型,它采用关键字enum来表达,其内部定义了一系列名称,通过有意义的名字比0/1/2这些数字能更有效地表达语义。下面是个Java定义枚举类型的代码例子:
enum Season { SPRING,SUMMER,AUTUMN,WINTER }
4、密封类
前面演示外部代码判断枚举值的时候,when语句末尾例行公事加了else分支。可是枚举类SeasonType内部一共只有四个枚举变量,when语句有四个分支就行了,最后的else分支纯粹是多此一举。出现此种情况的缘故是,when语句不晓得SeasonType只有四种枚举值,因此以防万一必须要有else分支,除非编译器认为现有的几个分支已经足够。
为解决枚举值判断的多余分支问题,Kotlin提出了“密封类”的概念,密封类就像是一种更加严格的枚举类,它内部有且仅有自身的实例对象,所以是一个有限的自身实例集合。或者说,密封类采用了嵌套类的手段,它的嵌套类全部由自身派生而来,仿佛一个家谱明明白白列出来某人有长子、次子、三子、幺子。定义密封类时使用关键字sealed标记,具体的密封类定义代码如下所示:
sealed class SeasonSealed {
//密封类内部的每个嵌套类都必须继承该类
class Spring (var name:String) : SeasonSealed()
class Summer (var name:String) : SeasonSealed()
class Autumn (var name:String) : SeasonSealed()
class Winter (var name:String) : SeasonSealed()
}
5、数据类
1、定义实体类的每个字段,以及对字段进行初始赋值的构造函数;
2、定义每个字段的get/set方法;
3、在判断两个数据对象是否相等时,通常要每个字段都比较一遍;
4、在复制数据对象时,如果想修改某几个字段的值,得再加对应数量的赋值语句;
5、在调试程序时,为了解数据对象里保存的字段值,得手工把每个字段值都打印出来;
6、模板类的应用如此广泛,Kotlin自然而然保留了它,并且写法与Java类似,一样在类名后面补充形如“”或者“<A, B>”的表达式,表示这里的类型待定,要等创建类实例时再确定具体的变量类型。待定的类型可以有一个,如ArrayList;可以有两个,如HashMap;也可以有三个或者更多,如AsyncTask。举个例子,森林里有一条小河,小河的长度可能以数字形式输入(包括Int、Long、Float、Double),也可能以字符串形式输入(String类型)。如果输入的是数字长度,则长度单位采取“m”;如果输入的是字符串长度,则长度单位采取“米”。按照以上需求编写名为River的模板类,具体的类定义代码如下:
//在类名后面添加“”,表示这是一个模板类
class River<T> (var name:String, var length:T) {
fun getInfo():String {
var unit:String = when (length) {
is String -> "米"
//Int、Long、Float、Double都是数字类型Number
is Number -> "m"
else -> ""
}
return "${name}的长度是$length$unit。"
}
}
7、总结一下,本文介绍了Kotlin的六种特殊函数,首先嵌套类和内部类都定义在某个外部类的内部,区别在于能否访问外部类的成员;其次枚举类和密封类都提供了有序的枚举值集合,区别在于密封类的定义更加严格;再次是帮助开发者摆脱搬砖命运的数据类;最后是解决未定参数类型的模板类(也叫泛型类)。