Kotlin学习笔记——对象表达式与对象声明

有时候,我们需要在不显示声明子类的前提下,需要声明一个类的对象,并对这个类做了少许的改动,在Java中,使用匿名内部类来实现,而在Kotlin中,用对象表达式对象声明来实现。

对象表达式

要创建一个继承自某个类(或某些类)的匿名对象,我们可以在这样写:

fun main(args: Array<String>) {
    // 创建一个继承自Listener类的匿名对象
   setListener(object : Listener() {
       override fun onChange() {
           TODO("not implemented")
       }
   })
}

fun setListener(l: Listener) {
    // ....
}

abstract class Listener {
    abstract fun onChange()
}

在Kotlin中,匿名对象是可以同时继承多个类或者接口的

  • 如果超类是一个接口(interface),那么才创建匿名对象时,不需要括号(因为接口没有构造函数)
fun main(args: Array<String>) {
    // 创建一个继承自Listener接口,不需要括号,因为它没有构造函数
   setListener(object : Listener {
       override fun onChange() {
           TODO("not implemented")
       }
   })
}

fun setListener(l: Listener) {
    // ....
}

interface Listener {
    fun onChange()
}
  • 如果超类具有构造函数,必须使用适合的构造函数初始化它,默认构造函数是无参数,只需要在类名带括号就可以,如果有参数,必须传入参数。如果有多个超类,可以在冒号后面使用逗号分隔开
fun main(args: Array<String>) {
    // Listener超类具有带参数的构造函数,需要传入参数初始化,多个超类使用逗号分隔开
    setListener(object : Listener("tag"), Observer {
        override fun onChange() {
            TODO("not implemented")
        }

        override fun notifyChanged() {
            TODO("not implemented")
        }
    })
}

fun setListener(l: Listener) {
    // ....
}

abstract class Listener(val tag: String) {
    abstract fun onChange()
}

interface Observer {
    fun notifyChanged()
}
  • 在某些情况下,我们只需要一个对象而已,并不需要任何的超类,可以这样简写
fun main(args: Array<String>) {

    // 只是需要个对象,不需要继承任何超类
    var point = object {
        var x: Int = 0
        var y: Int = 0

        override fun toString(): String {
            return String.format("Point(%d, %d)", x, y)
        }
    }

    point.x = 123
    point.y = 333

    println(point)
}
  • 请注意,匿名对象可以用作只在本地和私有作用域中声明的类型。如果你使用匿名对象作为公有函数的返回类型或者用作公有属性的类型,那么该函数或属性的实际类型会是匿名对象声明的超类型,如果你没有声明任何超类型,就会是 Any。在匿名对象中添加的成员将无法访问。
fun main(args: Array<String>) {
    val x = publicFun().x // 错误
    val x2 = privateFun().x // 可正确访问
}


fun publicFun() = object {
    // 匿名对象作为共有函数的返回值,没有声明具体超类,解析为Any,将无法访问对象的内部属性
    val x = 222
}

private fun privateFun() = object {
    // 匿名对象作为私有函数的返回值,没有声明具体超类,但可以访问对象的内部属性
    val x = 333
}
  • 跟 Java 匿名内部类一样,对象表达式中的代码可以访问来自包含它的作用域的变量。 (与 Java 不同的是,这不局限于 final 或实际相当于 final 的变量。)
fun main(args: Array<String>) {

    var times: Int = 0

    // Listener超类具有带参数的构造函数,需要传入参数初始化,多个超类使用逗号分隔开
    setListener(object : Listener("tag"), Observer {
        override fun onChange() {
            times++ // 可以访问来自包含他的作用域的变量,该变量不局限于final类型
        }

        override fun notifyChanged() {
            TODO("not implemented")
        }
    })
}

fun setListener(l: Listener) {
    // ....
}

abstract class Listener(val tag: String) {
    abstract fun onChange()
}

对象声明

  • 对象声明总是在 object 关键字后跟一个名称,就像变量声明一样。对象声明不是一个表达式,不能用在赋值语句的右边。

  • 在Kotlin中,通过对象声明实现单例对象的声明。

import kotlin.collections.ArrayList

// 单例
object NetworkManager {
    val networkObservers: ArrayList<NetworkObserver> = ArrayList<NetworkObserver>()

    fun registerObserver(observer: NetworkObserver) {
        // TODO
    }
}

fun main(args: Array<String>) {

    val observer = object: NetworkObserver() {
        override fun onNetworkChanged() {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }
    }

    NetworkManager.registerObserver(observer)
}

注意:对象声明不能在局部作用域中(即直接嵌套在函数内部),但是它们可以嵌套到其他对象声明或非内部类中。

伴生对象

  • 伴生对象声明使用关键字companion标识(对象声明有关键字object,伴生对象是在对象声明的基础上增加了一个关键字)
class Car {
    companion object Factory {
        fun create(): Car = Car()
    }
}

注意:
1. 伴生对象只能在类文件内部声明;
2. 在一个类中只能声明一个伴生对象.

  • 伴生对象可以直接使用类名作为限定符来调用(可以忽略掉伴生对象名)
fun main(args: Array<String>) {

    val car1 = Car.Factory.create() // 完整调用

    val car2: Car = Car.create() // 可以直接使用类名作为限定符调用,忽略伴生对象名
}
  • 伴生对象的声明可以直接省略掉名称,这时调用使用Companion
fun main(args: Array<String>) {

    val car1 = Car.Companion.create() // 伴生对象的声明省略掉了名称,调用使用Companion

    val car2: Car = Car.create() // 或者直接使用类名调用
}

class Car {
    // 声明的伴生对象没有名称,调用使用Companion
    companion object {
        fun create(): Car = Car()
    }
}
  • 更绝的是,可以使用伴生对象自身所用的类的名称来对对该类的伴生对象进行的引用,无论伴生对象是否命名都可以。
fun main(args: Array<String>) {

    val car1 = Car.Companion.create()

    val car2: Car = Car.create()
    
    val car3 = Car // 可以直接使用伴生对象自身所用类的类名来引用,无论伴生对象是否设置了名称
}

class Car {
    companion object {
        fun create(): Car = Car()
    }
}
  • 伴生对象看起来像是静态变量,其实并不是这样,因为它还可以实现接口
fun main(args: Array<String>) {

    val car1 = Car.Companion.create()

    val car2: Car = Car.create()

    val car3 = Car
}

class Car {
    companion object : Factory<Car> { // 伴生对象可以实现接口
        override fun create(): Car = Car()
    }
}

interface Factory<T> {
    fun create(): T
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值