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

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

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

一、对象表达式

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

interface OnClickListener {
    fun onClick()
}

fun main(args: Array) {
    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) {
    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) {
    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) {
    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) {
    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) {
    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) {
//    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) {
    // 访问伴生对象扩展属性
    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]

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

你可能感兴趣的:(Kotlin面向对象 (8)✔️强大的object关键字)