在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 list = Arrays.asList("rebpack", "score", "card");
Collections.sort(list, new Comparator() {
@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 {
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 { 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核心编程