kotlin面向对象总结-伴生对象

在Java中,static修饰的内容属于类,而不属于具体对象。

在Kotlin中,引入了全新的关键字object,代替使用static的场景。Kotlin中伴生对象——companion object两个关键字。

伴生对象,“伴生”是相较于一个类而言的,,意为伴随某个类的对象,它属于这个类所有,因此伴生对象跟Java中static修饰效果性质一样,全局只有一个单例。它需要声明在类的内部,在类被装载时会被初始化。

class PayPrice(val name: String, val count: Int, val type: Int) {

    companion object {
        val TYPE_REDPACK = 0
        val TYPE_COUPON = 1

        fun isRedpack(price: PayPrice): Boolean {
            return price.type == TYPE_REDPACK
        }
    }
}

fun main() {
    val price = PayPrice("红包", 10, PayPrice.TYPE_REDPACK)
    println(PayPrice.isRedpack(price))
}

true

companion object用花括号包裹了所有的静态属性和方法,使得它可以与PayPrice类的普通方法和属性区分开来,最后可以使用点号来对一个类的静态成员进行调用。

伴生对象的另一个作用是可以实现工厂方法模式。

class PayPriceFactory private constructor(val name: String, val count: Int, val type: Int) {

    companion object {
        val TYPE_COMMON = 1
        val TYPE_REDPACK = 2
        val TYPE_COUPON = 3

        val defaultCommonPrice = PayPriceFactory("普通奖品", 10, PayPriceFactory.TYPE_COMMON)

        fun newRedpackPrice(name: String, count: Int) =
            PayPriceFactory(name, count, PayPriceFactory.TYPE_REDPACK)

        fun newCouponPrice(name: String, count: Int) =
            PayPriceFactory(name, count, PayPriceFactory.TYPE_COUPON)

        fun defaultCommonPrice() = defaultCommonPrice
    }

    fun printPriceInfo() {
        println("name:$name,count:$count,type:$type")
    }
}

fun main() {

    val redpackPrice = PayPriceFactory.newRedpackPrice("红包", 10)
    redpackPrice.printPriceInfo()
    val couponPrice = PayPriceFactory.newCouponPrice("十元代金券", 10)
    couponPrice.printPriceInfo()
    val commonPrice = PayPriceFactory.defaultCommonPrice()
    commonPrice.printPriceInfo()
}

伴生对象是Kotlin中用来替代static关键字的一种方式,任何在Java类内部用static定义的内容都可以用kotlin中的伴生对象来实现。两者类似的是:一个类的伴生对象跟一个静态类一样,全局只能有一个。

如何通过object更优雅地实现Java中的单例模式?

Java中实现一个最基本的单例模式(忽略多线程情况),需要static关键字,同时必须将构造函数私有化。

在Kotlin中,由于object的存在,可以直接使用它来实现单例。

object PayDatabaseConfig {
    var host: String = "127.0.0.1"
    var port: Int = 3306
    var username: String = "root"
    var password: String = "12"
}

fun main() {
    val payDatabaseConfig = PayDatabaseConfig
    println("${payDatabaseConfig.host},${payDatabaseConfig.port},${payDatabaseConfig.username},${payDatabaseConfig.password}")
}

127.0.0.1,3306,root,12

由于object全局声明的对象只有一个,所以它并不用语法上的初始化,甚至都不需要构造方法。因此,object创造的是天生的单例。

单例也和普通类一样可以实现接口和继承类,所以可以看成是一种不需要初始化的类,当然全局只有一个。

object除了表现在单例对象以及上面说的伴生对象之外,还有一个作用就是替代Java中的匿名内部类。

通过object改善Java中的匿名内部类。

Java代码

public void sort() {
    List<String> list = Arrays.asList("rebpack", "score", "card");

    Collections.sort(list, new Comparator<String>() {

        @Override
        public int compare(String o1, String o2) {
            if (o1 == null) {
                return -1;
            }
            if (o2 == null) {
                return 1;
            }
            return o1.compareTo(o2);
        }
    });

    for (String s : list) {
        System.out.println(s);

    }
}

public static void main(String[] args) {
    TestObjectClassDemo demo = new TestObjectClassDemo();
    demo.sort();
}

card
rebpack
score

 Kotlin对象表达式

val comparator = object : Comparator<String> {
    override fun compare(o1: String?, o2: String?): Int {
        if (o1 == null) {
            return -1
        } else if (o2 == null) {
            return 1
        } else {
            return o1.compareTo(o2)
        }
    }
}

fun main() {
    val list = listOf("redpack", "score", "card")
    Collections.sort(list, comparator)
    
    list.forEach {
        println(it)
    }
}

object表达式跟Java的匿名内部类很相似,但是可以发现,object表达式可以赋值给一个变量,这在重复使用的时候可以减少很多代码。object可以继承类和实现接口,匿名内部类只能继承一个类及实现一个接口,而object表达式却没有这个限制。

用于代替匿名内部类的object表达式在运行中不像单例模式一样(全局只有一个对象),匿名内部类与object表达式并不是对任何场景都适合的,Java8引入的Lambda表达式对有些场景实现起来更加合适,比如接口中只有单个方法对实现。Kotlin 天然支持Lambda表达式。

通过lambda表达式进行改造:

val comparator2 = Comparator<String> { s1, s2 ->
    if (s1 == null) return@Comparator -1
    else if (s2 == null) return@Comparator 1
    s1.compareTo(s2)
}

fun main() {
    val list = listOf("redpack", "score", "card")
    Collections.sort(list, comparator2)

    list.forEach {
        println(it)
    }
}

card
redpack
score

对象表达式与Lambda表达式哪种更适合代替匿名内部类?

当你的匿名内部类使用的类接口只需要实现一个方法时,使用Lambda表达式更合适;当匿名内部类内有多个方法实现的时候,使用object表达式更加合适。

参考Kotlin核心编程

发布了179 篇原创文章 · 获赞 175 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/zhangying1994/article/details/104235831