日常开发中,经常会编写一些 Bean 类,用于描述一类事物,Bean 中会包含事物的属性,有的 Bean 类还会提供一系列"工具方法"来帮助开发者快速创建 Bean 对象。
就比如在 Java 中,Integer.valueOf() 就可以帮助开发者快速将字符串转成整型,进入源码一看,可以发现这是一个使用了 static 关键字修饰的静态方法:
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
可是,在 Kotlin 中是没有 static 关键字的!!!
想要实现通过 类名.xxx() 的方式调用工具方法,可以使用上述的 object 类,但 object 本身就是一个对象,我们无法在外部调用其构造方法,既然想拥有 object 类(通过类名调用方法)与 class 类(外部可以调用构造方法)的两种特性,这时伴生对象 companion object 就完全可以满足这个需求:
class Rectangle(val width: Int, val height: Int) {
companion object {
fun ofSize(width: Int, height: Int): Rectangle {
return Rectangle(width, height)
}
fun ofRectangle(rectangle: Rectangle): Rectangle {
return Rectangle(rectangle.width, rectangle.height)
}
}
}
fun main() {
val width = 4
val height = 5
val rectangle1 = Rectangle(width, height) // 调用构造方法
val rectangle2 = Rectangle.ofRectangle(rectangle1) // 通过类名调用方法
val rectangle3 = Rectangle.ofSize(width, height) // 通过类名调用方法
}
伴生对象顾名思义,就是与类一起诞生的对象,类一加载,它的伴生对象也就被创建了,每个类都可以对应一个伴生对象,并且该伴生对象的成员全局独一份,也就是说伴生对象 companion object 也是一种单例,再次借助 Show Kotlin Bytecode 工具,将上述 Kotlin 代码反编译成 java 代码如下:
public final class Rectangle {
...
public static final Rectangle.Companion Companion = new Rectangle.Companion((DefaultConstructorMarker)null);
public static final class Companion {
@NotNull
public final Rectangle ofSize(int width, int height) {
return new Rectangle(width, height);
}
@NotNull
public final Rectangle ofRectangle(@NotNull Rectangle rectangle) {
Intrinsics.checkParameterIsNotNull(rectangle, "rectangle");
return new Rectangle(rectangle.getWidth(), rectangle.getHeight());
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
可以发现,Kotlin 中的 companion object 其实对应到 Java 中也就只是 Rectangle 的一个静态内部类实例而已。