Kotlin自学之旅(五)特殊类

数据类

在Java中,我们经常创建一些只保存数据的类。 在这些类中,一些标准方法往往是从数据机械推导而来的。这些方法可以通过IDE生成,但在 Kotlin 中可以更方便的做到,只需要给类添加 data 修饰符,将其声明成数据类

data class User(val name: String, val age: Int)

编译器自动从主构造函数中声明的所有属性导出以下成员:

  • equals()/hashCode() 对;
  • toString()。格式为 “User(name=John, age=42)”;
  • componentN() 。按声明顺序对应于所有属性;
  • copy() ;
    toString()很简单,componentN()方法在上一章已经说过,而equals()、hashCode() 两个方法会将所有在主构造方法中声明的参数加入实现,生成的equals方法会检测所有的属性是否相等,hashcode方法会返回一个根据所有属性生成的哈希值。没有在主构造方法中声明的属性不会加入到这两个方法的计算中。

而copy方法用于简化复制一个对象并改变它的一些属性的操作。对于上面的User类,它的实现方式和使用方法如下:

//声明
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)

//使用
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

此外,请注意,如果在数据类体中有显式实现 equals()、 hashCode() 或者 toString(),或者这些函数在父类中有 final 实现,那么不会生成这些函数,而会使用现有函数;但如果超类型具有 open 的 componentN() 函数并且返回兼容的类型, 那么会为数据类生成相应的函数,并覆盖超类的实现。如果超类型的这些函数由于签名不兼容或者是 final 而导致无法覆盖,那么会报错。


嵌套类

像Java一样,在Kotlin中可以在一个类中声明另一个类,这样做有助于封装一个辅助类或者整合代码。

class Outer {
    private val bar: Int = 1
    inner class Inner {
        fun foo() = bar
    }
}

val demo = Outer().Inner().foo() // == 1

和Java不同的是,Kotlin默认嵌套类是没有持有外部类的引用的,如果要把嵌套类变成持有外部类引用的内部类,可以使用 inner 修饰符。这正好与Java相反。嵌套类和内部类在Java和Kotlin中的对应关系如下表:

类在另一个类B中声明在Java中在Kotlin中
嵌套类(不储存外部类的引用)static class Aclass A
内部类(储存外部类的引用)class Ainner class A

在Kotlin中引用外部类实例的语法也和Java不同,需要使用 this@Outer 从 Inner 类去访问 Outer 类:

class Outer {
    inner class Inner {
        fun getOuterReference():Outer = this@Outer
    }
}

枚举类

枚举通过 enum 声明。枚举类的最基本的用法是实现类型安全的枚举:每个枚举常量都是一个对象。枚举常量用逗号分隔:

enum class Direction {
    NORTH, SOUTH, WEST, EAST
}

因为每一个枚举都是枚举类的实例,所以他们可以是这样初始化过的:

enum class Color(val rgb: Int) {
        RED(0xFF0000),
        GREEN(0x00FF00),
        BLUE(0x0000FF)
}

枚举不是值的列表,我们可以给枚举类声明属性和方法,但在枚举类中定义方法的时候,要使用分号将枚举常量列表和方法定义分开:

enum class Color(val r: Int, val g:Int, val b: Int) {
    RED(255,0,0),
    GREEN(0,255,0),
    BLUE(0,0,255);

    fun rgb() = (r * 256 + g) * 256 + b;
}

枚举类经常配合 when 使用,如果分支已经将所有枚举覆盖,这时 when 可以不加 else

fun show(color: Color) = when(color) {
    Color.RED -> println("it is red!")
    Color.GREEN -> println("it is green!")
    Color.BLUE -> println("it is blue!")
}

就像在 Java 中一样,Kotlin 中的枚举类也有合成方法允许列出定义的枚举常量以及通过名称获取枚举常量。这些方法的签名如下(假设枚举类的名称是 EnumClass):

EnumClass.valueOf(value: String): EnumClass
EnumClass.values(): Array<EnumClass>

如果指定的名称与类中定义的任何枚举常量均不匹配,valueOf()方法将抛出 IllegalArgumentException 异常。


密封类

密封类用来表示受限的类继承结构:当一个值为有限集中的类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。
要声明一个密封类,需要在类名前面添加 sealed 修饰符。虽然密封类也可以有子类,但是所有子类都必须在与密封类自身相同的文件中声明。(在 Kotlin 1.1 之前, 该规则更加严格:子类必须嵌套在密封类声明的内部)。

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

使用密封类需要注意以下几个点:

  • 一个密封类是自身抽象的,它不能直接实例化并可以有抽象(abstract)成员。
  • 密封类不允许有非-private 构造函数(其构造函数默认为 private)。
  • 扩展密封类子类的类(间接继承者)可以放在任何位置,而无需在同一个文件中。

使用密封类的关键好处和枚举类相像,使用 when 表达式 的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了。当然,这只有当你用 when 作为表达式(使用结果)而不是才有用,如果是作为语句的话,那就怎样都行了。


总结

本文介绍了Kotlin中几种特别的类,涉及到的关键字有:

关键字说明
enum声明一个枚举
data指示编译器为类生成典型成员
inner允许在嵌套类中引用外部类实例
sealed声明一个密封类(限制子类化的类)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值