Kotlin学习系列(七)强大的object关键字

本系列内容均来自《Kotlin从小白到大牛》一书,感谢作者关东升老师。
object关键字主要用于声明一个类的同时创建这个类的对象。 具体而言它有三个方面的应用: 对象表达式、 对象声明和伴生对象。

1 对象表达式

object关键字可以声明对象表达式, 对象表达式用来替代Java中的匿名内部类。 就是在声明一个匿名类, 并同时创建匿名类的对象。
对象表达式示例如下:

//声明OnClickListener接口
interface OnClickListener {
    fun onClick()
} 
fun main(args: Array) {
    var i = 10
    val v = View()
// 对象表达式作为函数参数
    v.handler(object : OnClickListener { 
        override fun onClick() {
            println("对象表达式作为函数参数...")
            println(++i) 
        }
    })
}

上述代码第中v.handler函数的参数是对象表达式, object说明表达式是对象表达式, 该表达式声明了一个实现OnClickListener接口的匿名类, 同时创建对象。 另外,在对象表达式中可以访问外部变量, 并且可以修改, 见代码。
对象表达式的匿名类可以实现接口, 也可以继承具体类或抽象类, 示例代码如下:

//声明Person类
open class Person(val name: String, val age: Int)
fun main(args: Array) {
//对象表达式赋值
    val person = object : Person("Tony", 18), OnClickListener {②
        //实现接口onClick函数
        override fun onClick() {
            println("实现接口onClick函数...")
        }
        //重写toString函数
        override fun toString(): String {
            return ("Person[name=$name, age=$age]")
        }
    }
    println(person)
}

上述代码声明一个Person具体类, 代码第②行是声明对象表达式, 该表达式声明实现OnClickListener接口, 且继承Person类的匿名类, 之间用逗号( ,) 分隔。Person("Tony", 18)是调用Person构造函数。 注意接口没有构造函数, 所以在表达式中OnClickListener后面没有小括号。
有的时候没有具体的父类也可以使用对象表达式, 示例代码如下:

fun main(args: Array) {
//无具体父类对象表达式
    var rectangle = object { ①
        // 矩形宽度
        var width: Int = 200
        // 矩形高度
        var height: Int = 300
        //重写toString函数
        override fun toString(): String {
            return ("[width=$width, height=$height]")
        }
    } 
    println(rectangle)
}

代码是声明一个对象表达式, 没有指定具体的父类和实现接口, 直接在object后面大括号中编写类体代码。

2 对象声明

单例设计模式( Singleton) 可以保证在整个的系统运行过程中只有一个实例, 单例设计模式在实际开发中经常使用的设计模式。 Kotlin把单例设计模式上升到语法层面, 对象声明将单例设计模式的细节隐藏起来, 使得在Kotlin中使用单例设计模式变得非常的简单。
对象声明示例代码如下:

interface DAOInterface {
    //插入数据
    fun create(): Int
    //查询所有数据
    fun findAll(): Array?
}

object UserDAO : DAOInterface { ①
    //保存所有数据属性
    private var datas: Array? = null
    override fun findAll(): Array? {
//TODO 查询所有数据
        return datas
    }
    override fun create(): Int {
//TODO 插入数据
        return 0
    }
}

fun main(args: Array) {
    UserDAO.create() ②
    var datas = UserDAO.findAll() ③
}

上述代码第①行是对象声明, 声明UserDAO单例对象, 使用object关键字后面是类名。在对象声明的同时可以指定对象实现的接口或父类, 本例中指定实现DAOInterface接口。 在类体中可以有自己的成员函数和属性。 在调用时, 可以通过类名直接访问单例对象的函数和属性, 见代码第②行和第③行。
提示 对象声明不能嵌套在其他函数中, 但可以嵌套在其他类中或其他对象声明中。
示例代码如下:

object UserDAO : DAOInterface {
    //保存所有数据属性
    private var datas: Array? = null
    override fun findAll(): Array? {
//TODO 查询所有数据
        return datas
    }
    override fun create(): Int {
// object Singleton { ①
// val x = 10
// }
        return 0;
    }
    object Singleton { ②
        val x = 10
    }
}
class Outer {
    object Singleton { ③
        val x = 10
    }
}
fun main(args: Array) {
    println(UserDAO.Singleton.x)
// object Singleton { ④
// val x = 10
// }
}

上述代码第①行和第④行试图在函数中嵌入Singleton对象声明, 则会发生编译错误。 代码第②行是Singleton对象声明嵌入在UserDAO对象声明中。 代码第③行是Singleton对象声明嵌入在Outer类中。

3 伴生对象

在Java类有实例成员和静态成员, 实例成员隶属于类的个体, 静态成员隶属于类本身。 例如: 有一个Account( 银行账户) 类, 它有三个成员属性: amount( 账户金额) 、interestRate( 利率) 和owner( 账户名) 。 在这三个属性中, amount和owner会因人而异, 对于不同的账户这些内容是不同的, 而所有账户的interestRate都是相同的。amount和owner成员属性与账户个体有关, 称为“实例属性”, interestRate成员属性与个体无关, 或者说是所有账户个体共享的, 这种变量称为“静态属性”或“类属性”。

01. 声明伴生对象

在很多语言中静态成员的声明使用static关键字修饰, 而Kotlin没有static关键 字, 也没有静态成员, 它是通过声明伴生对象实现Java静态成员访问方式
示例代码如下:

class Account {
    // 实例属性账户金额
    var amount = 0.0
    // 实例属性账户名
    var owner: String? = null
    // 实例函数
    fun messageWith(amt: Double): String {
//实例函数可以访问实例属性、 实例函数、 静态属性和静态函数
        val interest = Account.interestBy(amt) ①
        return "${owner}的利息是$interest"
    }
    companion object { ②
        // 静态属性利率
        var interestRate: Double = 0.0 ③
        // 静态函数
        fun interestBy(amt: Double): Double {
            ④
// 静态函数可以访问静态属性和其他静态函数
            return interestRate * amt
        }
        // 静态代码块
        init {
            ⑤
            println("静态代码块被调用...")
// 初始化静态属性
            interestRate = 0.0668
        }
    } ⑥
}
fun main(args: Array) {
    val myAccount = Account() ⑦
// 访问伴生对象属性
    println(Account.interestRate) ⑧
// 访问伴生对象函数
    println(Account.interestBy(1000.0)) ⑨
}

上述代码第②行~第⑥行是声明伴生对象, 使用关键字companion和object。 作为对象可以有成员属性和函数, 代码第③行是声明interestRate属性, 伴生对象的属性可以在容器类( Account) 外部通过容器类名直接访问, 见代码第⑧行Account.interestRate表达式, 这种表达式形式与Java等语言中访问静态属性是类似的。 类似代码第④行声明伴生对象函数, 调用该属性见代码第①行和第⑨行。代码第⑤行是伴生对象的init初始化代码块, 它相当于Java中的静态代码, C#中的静态构造函数, 它可以初始化静态属性, 该代码块会在容器类Account第一次访问时调用, 代码第⑦行是第一次访问Account类, 此时会调用伴生对象的init初始化代码块。
注意 伴生对象函数可以访问自己的属性和函数, 但不能访问容器类中的成员属性和函数。 容器类可以访问伴生对象的函数和属性。

02. 伴生对象非省略形式

在上面的示例中事实上省略的伴生对象名字, 声明伴生对象时还可以添加继承父类或实现接口。 示例代码如下:


//声明OnClickListener接口
interface OnClickListener {
    fun onClick()
}

class Account {
    // 实例属性账户金额
    var amount = 0.0
    // 实例属性账户名
    var owner: String? = null

    // 实例函数
    fun messageWith(amt: Double): String {
//实例函数可以访问实例属性、 实例函数、 静态属性和静态函数
        val interest = Account.interestBy(amt)
        return "${owner}的利息是${interest}"
    }

    companion object Factory : Date(), OnClickListener { ①
        override fun onClick() {
        }

        // 静态属性利率
        var interestRate: Double = 0.0

        // 静态函数
        fun interestBy(amt: Double): Double {
// 静态函数可以访问静态属性和其他静态函数
            return interestRate * amt
        }

        // 静态代码块
        init {
            println("静态代码块被调用...")
// 初始化静态属性
            interestRate = 0.0668
        }
    }
}

fun main(args: Array) {
    val myAccount = Account()
// 访问伴生对象属性
    println(Account.interestRate)
    println(Account.Factory.interestRate) ②
// 访问伴生对象函数
    println(Account.interestBy(1000.0))
    println(Account.Factory.interestBy(1000.0)) ③
}

上述代码第①行是声明伴生对象, 其中Factory是伴生对象名, Date()是继承
Date类, OnClickListener是实现该接口。 一旦显示指定伴生对象名后, 在调用时可以加上伴生对象名, 见代码第②行和第③行, 当然省略伴生对象名也可以调用它的属性和函数。

03. 伴生对象扩展

伴生对象中可以添加扩展函数和属性, 示例代码如下:

//伴生对象声明扩展函数
fun Account.Factory.display() {
println(interestRate)
}
...
//访问伴生对象扩展函数
Account.Factory.display()
Account.display()

从上述代码可见, 调用伴生对象的扩展函数与普通函数访问没有区别。

你可能感兴趣的:(Kotlin学习系列(七)强大的object关键字)