Kotlin面向对象 (8)✔️强大的object关键字

  • 对象表达式
  • 对象声明 (单例,隶属于——饿汉模式)
  • 伴生对象

object 关键字主要在声明一个类的同时创建这个类的对象。具体而言它有三方面应用:对象表达式、对象声明 和 伴生对象。

一、对象表达式

  • object 关键字可以声明对象表达式,对象表达式用来替代 Java 中的匿名内部类。
class View {
    fun handler(listener: OnClickListener?) {
        listener?.onClick()
    }
}

interface OnClickListener {
    fun onClick()
}

fun main(args: Array<String>) {
    var i = 0;
    val view = View()
    view.handler(object :OnClickListener {
        override fun onClick() {
            println("对象表达式作为函数参数... i=$i")
            i++
        }
    })
}

  上述代码中 view.handler 函数的参数是对象表达式,object 说明表达式是对象表达式,该表达式声明了一个实现 OnClickListener 接口的匿名类,同时创建对象。

  • 对象表达式的匿名类可以实现接口,也可以继承具体类或抽象类。
open class Person(val name: String, var age: Int) // 1️⃣

fun main(args: Array<String>) {
    val person = object :Person("小三", 18), OnClickListener { // 2️⃣

        override fun onClick() {
            println("实现接口 onClick 函数...")
        }

        override fun toString(): String {
            return "[name: $name, age: $age]"
        }
    }

    person.onClick()

    println(person)
}

2019-05-30 18:41:17.171 31741-31741/cn.ak.kot I/System.out: 实现接口 onClick 函数...
2019-05-30 18:41:17.171 31741-31741/cn.ak.kot I/System.out: [name: 小三, age: 18]

  上面代码第1️⃣行是声明一个Person的具体类,代码第2️⃣行是声明对象表达式,该表达式声明实现了 OnClickListener 接口,且继承 Person 类的匿名类,之间用逗号 (,) 分隔。Person("小三", 18) 是调用 Person 的构造函数。

  • 没有具体的父类也可以使用对象表达式。
fun main(args: Array<String>) {
    val rect = object {
        val x: Int = 10
        val y: Int
        
        init {
            y = 10
        }
        
        fun area() = x * y

        override fun toString(): String {
            return "[x:$x, y:$y]"
        }
    }
    
    println(rect.area())    // 100
    println(rect)           // [x:10, y:10]
}

二、对象声明 (单例)

  单例设计模式可以保证在整个的系统运行过程中只有一个实例,单例模式在开发中是经常使用的设计模式,所以kotlin把单例设计模式上升到语法层面。

  • 单例对象声明,object 关键字后面是类名,在对象声明的同时可以指定对象实现接口或父类,在类体中可以有自己的成员函数和属性,在调用时,可以通过类名直接访问单例对象的函数和属性。
open class EnglishTool(var name: String) {

    override fun toString(): String {
        return "[name: $name]"
    }
}

interface Upper {
    fun toUpper(word: String): String
}

object Toolkit: EnglishTool("英语"), Upper {

    override fun toUpper(word: String): String {
        return word.toUpperCase()
    }

    fun printUpperWord(word: String) {
        println(toUpper(word))
    }
}

fun main(args: Array<String>) {
    Toolkit.printUpperWord("hello ak")
    println(Toolkit)

    Toolkit.name = "中文"
    println(Toolkit)
}

2019-05-30 19:12:01.819 3724-3724/cn.ak.kot I/System.out: HELLO AK
2019-05-30 19:12:01.819 3724-3724/cn.ak.kot I/System.out: [name: 英语]
2019-05-30 19:12:01.819 3724-3724/cn.ak.kot I/System.out: [name: 中文]
  • 为什么是饿汉模式?
// kotlin 单例代码
object Toolkit {
}
// kotlin 转换后的 java 代码
package cn.ak.kotmodule;

import kotlin.Metadata;

@Metadata(
   mv = {1, 1, 15},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\bÆ\u0002\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002¨\u0006\u0003"},
   d2 = {"Lcn/ak/kotmodule/Toolkit;", "", "()V", "kotlin_module"}
)
public final class Toolkit {
   public static final Toolkit INSTANCE;

   private Toolkit() {
   }

   static {
      Toolkit var0 = new Toolkit();
      INSTANCE = var0;
   }
}

  从 kotlin 解释出的 java 很直白的可以出是一个 饿汉模式。至于怎么查看 kotlin 解释出的 java 代码,请自行百度,本文中是开发工具是 Android Studio

  • object 对象声明不能嵌套在其他 函数 中, 但可以嵌套在其他 或 其他 object对象声明 中。
object Toolkit {

    fun doSomethings(): Int {
        // object Singleton { // 编译不过
        //     val x = 10
        // }
        return 0
    }

    object Singleton {
        val x = 10
    }
}

class Outer {
    object Singleton {
        val x = 10
    }
}

fun main(args: Array<String>) {
    println(Toolkit.Singleton.x)    // 10
    println(Outer.Singleton.x)      // 10
}

三、伴生对象

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

1、声明伴生对象
class Account {
    var amout = 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 { // 1️⃣
             println("静态代码块被调用")
             // 初始化静态属性
             interestRate = 0.0668
         }
     }
}

fun main(args: Array<String>) {
    val account = Account() // 2️⃣
    // 访问伴生对象属性
    println(Account.interestRate)
    // 访问伴生对象函数
    println(Account.interestBy(1000.0))
}
  • 伴生对象,使用关键字 companionobject 声明。作为对象可以有成员属性和函数,在容器类外访问伴生对象的属性和函数,可以通过容器类名直接访问。代码第1️⃣行是伴生对象的 init 初始化代码块,它相当于 Java 中的静态代码,它可以初始化静态属性,该代码块会在容器类第一次访问时调用,代码第2️⃣行是第一次访问 Account 类,此时会调用伴生对象的 init 初始化代码块。

  注意:伴生对象函数可以访问自己的属性和函数,但不能访问容器类中的成员属性和函数。容器类可以访问伴生对象的函数和属性。这一点与 Java 类中的静态成员和静态方法是一样的,只能访问本类的静态成员和其它的静态函数。

2、伴生对象非省略形式

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

open class Text(var name: String) {
    override fun toString(): String {
        return "[name: $name]"
    }
}

class GView {

    companion object GButton : Text("按钮"), OnClickListener {

        override fun onClick() {
            println("点击了伴生对象的onClick...")
        }

        init {
            println("伴生对象初始化代码块")
            name = "View的伴生对象Button的成员属性name=按钮"
        }
    }
}

fun main(args: Array<String>) {
//    val v = GView()
    println(GView.name)
    println(GView.GButton.name)

    GView.onClick()
    GView.GButton.onClick()
}

2019-05-30 23:44:45.237 12893-12893/com.ktln.cltor I/System.out: 伴生对象初始化代码块
2019-05-30 23:44:45.237 12893-12893/com.ktln.cltor I/System.out: View的伴生对象Button的成员属性name=按钮
2019-05-30 23:44:45.237 12893-12893/com.ktln.cltor I/System.out: View的伴生对象Button的成员属性name=按钮
2019-05-30 23:44:45.237 12893-12893/com.ktln.cltor I/System.out: 点击了伴生对象的onClick...
2019-05-30 23:44:45.238 12893-12893/com.ktln.cltor I/System.out: 点击了伴生对象的onClick...

  讲解:上面代码中 GButton 是伴生对象名,Text("按钮") 是继承 Text 类,OnClickLisener 是实现该接口。一旦显示指定伴生对象名后,在调用时可以加上伴生对象名,当然省略伴生对象名也可以调用它的属性和函数。

3、伴生对象扩展

伴生对象中可以添加扩展函数和属性。

val GView.GButton.length: Int
    get() = this.name.length

fun GView.GButton.display() {
    println("[name: $name, length: $length]")
}

fun main(args: Array<String>) {
    // 访问伴生对象扩展属性
    println(GView.GButton.length)
    println(GView.length)
    // 访问伴生对象扩展函数
    GView.GButton.display()
    GView.display()
}

2019-05-31 00:26:43.813 17030-17030/com.ktln.cltor I/System.out: 伴生对象初始化代码块
2019-05-31 00:26:43.814 17030-17030/com.ktln.cltor I/System.out: 27
2019-05-31 00:26:43.814 17030-17030/com.ktln.cltor I/System.out: 27
2019-05-31 00:26:43.814 17030-17030/com.ktln.cltor I/System.out: [name: View的伴生对象Button的成员属性name=按钮, length: 27]
2019-05-31 00:26:43.814 17030-17030/com.ktln.cltor I/System.out: [name: View的伴生对象Button的成员属性name=按钮, length: 27]

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容