Kt学习笔记(九)数据类、封闭类

文章目录

        • 一、数据类
          • 1.1、使用数据类
          • 1.2、对象复制
          • 1.3、数据类成员的解构
        • 二、封闭类

一、数据类

  数据类是 Kotlin 的一个语法糖。 Kotlin 编译器会自动为数据类生成一些成员函数,以提高开发效率

1.1、使用数据类

  如果在程序中使用数据库,或映射 JSON 数据,很可能会将查询结果集或 JSON 格式的数 据映射成 为对象,或将对象映射成为数据集或 JSON 格式的数据。无论是哪一种,我们总是需 要一个类来表示数据。例如,要表示用户的数据,需要建立一个 User 类,并通过构造器传入 两个参数值: name和 age。


class User(name : String,age : Int)
{

    var name : String  = name

    var age : Int  = age
}

  在User类中,主构造器有两个参数: name和age,在类中还定义了两个 属性: name和 age,并用主构造器的两个参数分别初始化了两个成员属性。
  对于表示数据的类来说,除了要保存数据外,还要进行一些操作, 如输出对象的值,比较对象是否 相等 。 Kotlin 代码前者会调用对象的 toString 函数,后者会调用对象的 equals 函数。

    /**
     * 输出user1 和user2 时,调用了这两个对象的toString函数,
     * 而toString函数默认会调用hashcode函数输出当前对象的hashcode,
     * 并在前面加上"类名@",就像User@
     */
    var user1 : User = User("mike",21)
    var user2 : User = User("mike",21)
    println(user1)
    println(user2)

    /**
     * 对象的 equals方法默认比较了对象之间的 hashcode,如果相同,就是一个对象,它们也就 相等了 。
     * 不过,在实际应用中,需要 toString 函数输出对象属性值,
     * 而 equals 函数通过属性值 是否相等进行比较。
     * 只有所有的属性值都相等,两个对象才会判定相等。
     */
    println(user1.equals(user2))

Kt学习笔记(九)数据类、封闭类_第1张图片

  为了使用 toString 函数输出 name 和 age 属性值,使用 equals 函数比较两个 User对象是否 相等,需要在 User类中重写这两个函数,代码如下:


class User(name : String,age : Int)
{

    var name : String  = name

    var age : Int  = age

    override fun toString(): String {
        return "User (name = ${name},age = ${age})"
    }

    override fun equals(other: Any?): Boolean {

        // 判断other是否为User对象, 如果不是User对象,直接返回false

        if(other is User)
        {
            //kt会自动转换数据类型, 根据is来判断,other现在已经是User对象了
            if(name == other.name && age == other.age)
            {
                return true
            }
        }else
        {
            return false
        }
        return false
    }

}

Kt学习笔记(九)数据类、封闭类_第2张图片

   User 类尽管可 以完全满足我们的要求,但如果每个表示数据 的类都要重写 toString和 equals方法,而且都要加属性,岂不是很麻烦,为了让开发效率更进 一步提升 , Kotlin 加入了数据类。所谓数据类,其实就是只定义必要的部分,其 余的部分 ,可以自动推导。
   从 User类的代码可以看出,只有 name 和 age 是必要的,其余的都可以自动推导,因此, 只需要在 User类中指定 name 和 age 即可。如何指定 name 和 age 呢?数据类规定,属性要通 过主构造器指定,而且数据类要在 class关键宇前面加 data。因此,如果前面的 User类用数据 类的写法 ,就变成了下面的样子。

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

Kt学习笔记(九)数据类、封闭类_第3张图片
User数据类中并没有类的实现体,实际上,数据类和普通类完全一样, 也可以拥有类的实现体, 数据类和普通类最大不同,就是数据类可以根据主构造器的参数自动生成相关的代码,因此, 如果一个 Kotlin类,同时兼有普通类,以及存储和管理数据的功能,建议直接使用数据类。

open class MyClass
{

}
data class User(val name : String,var age : Int):MyClass()
{
    fun process()
    {
        println("process")
    }
}

编写 一个数据类需要注意的事项如下 。

  • 主构造器至少要有 一个参数。
  • 主构造器的所有参数必须标记为 val或 var。
  • 数据类不能是抽象类 、 open 类、封闭( sealed)类或内部 (inner)类。
      由于数据类要求主构造器必须至少有一个参数 , 因此在数据类中,不可能存在没有参数的 主构造器, 要想让数据类拥有一个没有参数的构造器,有如下两种方法。
  • 为主构造器每一个参数都加上默认值 。
  • 添加一个没有参数的次构造器,并在调用主构造器时指定主构造器参数的默认值。
       其实这两种方法的效果是一样的,就是主构造器的每一个参数必须要有一个默认值才能实现没有参数的构造器。

为主构造器的每一个参数添加一个默认值

data class User(val name : String = "Bill",var age : Int = 20)

添加一个次构造器

data class User(val name : String = "Bill",var age : Int = 20):MyClass()
{
    //添加一个构造器  次构造器
    constructor():this("Bill",20)
    {
        
    }
    
    fun process()
    {
        println("process")
    }
}
1.2、对象复制

  在实际应用中 , 经常需要复制一个对象,然后修改它的一部分属性, 这就需要数据类提供 一种机制可以复制对象。在前面讲过, Kotlin编译器会为数据类 自动生成一些代码 (bytecode), 这些代码不仅包括 toS仕ing和 equals,还包括一个 copy 函数,该函数的作用就是复制数据类的实例。

  copy 函数的代码 ,核心代码就是下面的样子 。 很明显,在 copy 函数中,就是创建了一个当前数据类的实例, 并传入了 name和 age属性

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

Kt学习笔记(九)数据类、封闭类_第4张图片

Kt学习笔记(九)数据类、封闭类_第5张图片

使用数据类可直接调用copy()函数

1.3、数据类成员的解构

  现在先来解释一下什么叫数据类成员的解构 ,这里的关键是解构 ,也就是解除结构 。在数 据类中,用属性表示数据, 这些属性属于同一个数据类。要想使用这些属性,必须要首先引用 数据类对象。这里的解构就是指将这些数据对象中 的属性提取出来,分别赋给单个的变量 ,这 样就可以单独使用它们了


    val jane = MyUser(name = "Jane",age = 20)
    //将MyUser.name  MyUser.age 分别赋予name和age变量
    val (name,age) = jane
    println("$name , $age year of age")

Kt学习笔记(九)数据类、封闭类_第6张图片

二、封闭类

  封闭类也是 Kotlin 的一个语法糖。可以把它理解为枚举的扩展。一个封闭类,前面用 sealed关键字标识。可以有任意多个子类或对象。封 闭类的值只能是这些子类或对象 。使用封闭类的 好处主要是 when 表达式,不需要再使用 else 形式了。下面给出一个例子。

sealed class Expr

data class Const(val number : Double) : Expr()

data class Sum(val param1 : Expr,val param2 : Expr) : Expr

object NotANumber : Expr()

fun eval (expr: Expr) : Double = when(expr)
{
    is Const -> expr.number
    is Sum -> eval(expr.param1) + eval(expr.param2)
    NotANumber -> Double.NaN
    //在这里不要求使用else子句匹配其他情况
}

  在这段代码中 , Expr 是一个封闭类,该类有两个数据类( Const 和 Sum)和一个对象 CNotANumber)。下面的函数判断 Expr 类型参数 的种类,这里面只有 3 个种类: Const、 Sum 和 NotANumber

你可能感兴趣的:(Kotlin)