Kotlin开发笔记:伴生对象和数据类

Kotlin开发笔记:伴生对象和数据类

Kotlin开发笔记:伴生对象和数据类_第1张图片

简介:

前面的文章里我们已经介绍了如何创建类和类的成员变量,方法等。那么如果想要创建类级别的属性或者方法(就是类似于静态成员变量和方法)该如何实现呢?本篇文章将会介绍通过伴生对象实现以及数据类的相关内容。

伴生对象:

我们创建一个名为MachineOperator的类,这个类有一个类级别的属性和方法,是用伴生对象生成的。还记得我们之前说到过单例对象的创建就是使用object关键字,而伴生对象的创建也是类似,就是在object关键字之前再加上companion关键字:

class MachineOperator(val name:String) {

    fun checkin() = checkedId++
    fun checkout() = checkedId--


    companion object{
        var checkedId = 0

        fun minimumBreak() = "15 minutes every 2 hours"
    }
}

不过这里需要说明一下,每个类只能有一个伴生对象。伴生对象中的方法和属性也就类似于Java中的静态变量和方法或者也可以理解为Kotlin中的类单例对象。伴生对象中的属性是该类的所有实例共享的,所以说在多线程情况下可能需要额外进行同步等操作。

访问伴生对象中的属性和方法等是和Java中访问静态变量和方法的形式是一样的,我们用以下代码测试一下:

fun main() {
    val machine = MachineOperator("Mater").checkin()
    println(MachineOperator.checkedId)
    MachineOperator("Oliver").checkin()
    println(MachineOperator.checkedId)

    val str1 = MachineOperator.minimumBreak()
    val str2 = MachineOperator.minimumBreak()

    if(str1 === str2){
        println("yes")
    }else{
        println("no")
    }
}

最后输出的结果是:
Kotlin开发笔记:伴生对象和数据类_第2张图片
这也说明了所有实例都是共享伴生对象的。

访问整个伴生对象

有时候我们会需要访问整个伴生对象,这在伴生对象实现了某个接口时会经常出现,比如下面这个伴生对象就实现了Runnable接口:

    companion object:Runnable{
        var checkedId = 0

        fun minimumBreak() = "15 minutes every 2 hours".toString()
        override fun run(){
            println("run run run")
        }

如果我们想要访问这整个伴生对象,默认情况下就可以使用Companion字段,在我们没有指定伴生对象的名称时,这个伴生对象的名称就默认为Companion,比如我们可以这样访问:

fun main(){
	MachineOperator.Companion.run()
}

我们也可以指定伴生对象的名称,只要在object关键字后加上我们要取的名字即可:

class MachineOperator(val name:String) {

    fun checkin() = checkedId++
    fun checkout() = checkedId--


    companion object myComp:Runnable{
        var checkedId = 0

        fun minimumBreak() = "15 minutes every 2 hours".toString()
        override fun run(){
            println("run run run")
        }
    }
}

这种情况下我们就给我们的伴生对象取名为myComp,现在访问这整个伴生对象就是这样访问的:

fun main(){
	MachineOperator.myComp.run()
}

并非完全静态

可能我们会认为这个伴生对象中的属性方法是静态的,其实不然,它实际上还是一个类级别的单例对象,就如同object关键字所定义的一样。如果我们把它单纯认为是静态的可能会出现一些问题。

当我们引用伴生对象的成员时,Kotlin编译器会负责将调用路由到适当的单例实例。

数据类

Kotlin中的数据类是专用类,主要用于承载数据而不是行为。主构造函数需要使用val或者var至少定义一个属性,这里不允许使用non-val或者non-var。如果需要,可以在body{}中添加其他属性或者方法。不过在块体内定义的任何属性将不会在生成的equals(),hashCode()和toString()方法中使用。

定义数据类只需要在class之前加上data修饰符即可,下面就简单地创建了一个数据类,包含Id和name两个属性:

data class personn(val id:Int,val name:String) {
    
}

数据类将会自动生成一系列方法,比如equals(),hashCode()和toString()方法,我们可以来测试一下toString方法自动生成的:

fun main() {
    val p = personn(1,"Jake")
    val p1 = personn(2,"Mike")
    println(p)
    println(p1)
}

输出的内容是:
Kotlin开发笔记:伴生对象和数据类_第3张图片
除此之外,数据类还会自动生成copy方法,这个方法会创建一个新对象,并将接收端对象的所有属性复制到结果对象中。与equals(),hashCode()和toString()方法不同,copy方法会复制对象中的所有属性包括body{}块中的属性。同时我们在使用这个copy方法时还可以更改一些属性的内容:

fun main() {
    val p = personn(1,"Jake")
    val p1 = personn(2,"Mike")
    println(p)
    println(p1)
    val p2 = p.copy(name="ask")
    println(p2)
}

输出的结果是:
Kotlin开发笔记:伴生对象和数据类_第4张图片
需要说明的是这个自动生成的copy方法只是进行了浅拷贝,该方法不会深度复制内部引用的对象。

最后,数据类还会自动生成componentN的一系列方法,这一系列方法是用来进行解构的,比如我们可以进行以下解构:

fun main() {
    val p = personn(1,"Jake")
    val p1 = personn(2,"Mike")
    println(p)
    println(p1)
    val p2 = p.copy(name="ask")
    println(p2)

    val(sb,qq) = p2
    println("name is $qq,id is $sb")
}

最后解构的输出就是:
在这里插入图片描述

何时使用数据类

书中给出了何时应该使用数据类型的情况:

  1. 你在给数据建模,而不是行为
  2. 你希望生成equals(),hashCode(),toString(),和/或者copy(),因为如果愿意,你可以重写这些方法中的任何一个
  3. 主构造函数至少接收一个属性是有意义的–数据类不允许使用无参数的构造函数
  4. 主构造函数只接受属性是有意义的
  5. 你希望使用解构工具轻松地从对象中提取数据

总结

本章中我们主要介绍了伴生对象和数据类的相关内容。比较重要的就是伴生对象中的内容,主要是用来实现Java中类似于静态成员变量和方法的作用,但是伴生对象又不是简单的静态的。数据类是Kotlin中的一种特殊的模版类,可以方便地实现一些方法。

你可能感兴趣的:(Kotlin学习笔记,kotlin,笔记,开发语言)