一、伴生对象的目标
1、java的类中有静态成员或函数但是kotlin中没有静态成员或者函数
2、但是伴生对象依赖包级别的函数和对象声明,可以用来替代java中的静态成员和函数
3、伴生对象的本质是静态的
class User private constructor(val nickname: String) { companion object { val nameUsers = mutableMapOf<String, User>() fun newUser(nickname: String): User { if (!nameUsers.containsKey(nickname)) { nameUsers.put(nickname, User(nickname)) } return nameUsers.getValue(nickname) } } }
如上是对应的伴生对象的实例,如下是这段代码转换成java的类细节
public final class User { @NotNull public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null); @NotNull private final String nickname; @NotNull private static final Map nameUsers = (Map)(new LinkedHashMap()); private User(String nickname) { this.nickname = nickname; } @NotNull public final String getNickname() { return this.nickname; } // $FF: synthetic method public User(String nickname, DefaultConstructorMarker $constructor_marker) { this(nickname); } public static final class Companion { private Companion() { } @NotNull public final Map getNameUsers() { return User.nameUsers; } @NotNull public final User newUser(@NotNull String nickname) { Intrinsics.checkNotNullParameter(nickname, "nickname"); if (!this.getNameUsers().containsKey(nickname)) { this.getNameUsers().put(nickname, new User(nickname, (DefaultConstructorMarker)null)); } return (User)MapsKt.getValue(this.getNameUsers(), nickname); } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } }View Code
转换成代码比较多,这里使用折叠的方式;具体的细节是:
1、User类中创建了:public static final class Companion 嵌套类。同时在User类中创建累这个Companion的静态属性:public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null);
2、kotlin的User类中的伴生对象是匿名的,所以转成java使用的类名是:Companion;如果半生对象是有指定名字,比如:companion object CreateUser {。。。}, 则java中创建的嵌套类的类名则不是Companion而是CreateUser
3、通过1和2的描述则可以知道:在java中调用的kotlin伴生对象则通过User.Companion.newUser(nickname), 如果是非匿名则为User.具体名字.newUser(nickname)
二、伴生对象的格式
1、在类中声明
2、companion object +可选对象名字 {......}
3、直接能够伴生对象中的函数:class Person { companion object {。。。。}}, 则可以调用:Person.xxxxx
4、以上三点的例子
class Person(val name: String) { companion object Loader{ fun fromJson(json: String): String { return json } } } println(Person.Loader.fromJson("kkaaa")) println(Person.fromJson("kkaaa")) /* 打印的结果是: kkaaa kkaaa */
上面的Loader可以加也可以不加。可以根据自己的需要是否增加伴生对象的名称
三、伴生对象实现接口
通过一中对伴生对象的描述,转换为java之后是一个嵌套类,那么说明它也是可以进行继承
interface JsonFactory<T> { fun fromJson(json: String): T } class Person(val name: String) { companion object Loader: JsonFactory<Person>{ // 在伴生对象这里增加r: JsonFactory<Person>,同时实现fromJson函数 override fun fromJson(json: String): Person { //注意这里有override return Person(json) } } } fun <T> loadFromJson(factory: JsonFactory<T>): T { return factory.fromJson("kkqqq") } loadFromJson(Person) // 这里的调用,直接传入Person; 咱们对companion的调用是Person.fromJson,则是把Person当成对象,所以直接传入Person是可以的
那么在调用loadFromJson函数是否可以传入Person.Loader()? 这里给的报错是:error: not enough information to infer type variable T
四、伴生对象扩展
1、类似普通的扩展函数,伴生对象也允许扩展函数。如果类中有伴生对象,那么在外部你可以定义该伴生对象的扩展函数
class Person(val name: String) { companion object {} // 这里需要一个伴生对象,即使是空对象否则没法做扩展 } // 因为上面的半生对象是匿名,所以这里使用默认的Companion // 如果上面使用非匿名如: companion object Loader {} // 那么这里就需要使用:Person.Loader.fromJson fun Person.Companion.fromJson(json: String): Person { return Person(json) }
五、对象表达式:匿名内部类
1、object除了能够用来作为伴生对象还能够用来声明匿名对象
2、匿名对象可以实现多个接口或者不实现接口,java的匿名内部类只能扩展一个类或实现一个借口
val linstener = object: MouseAdapter() { // 这里使用object override fun mouseClicked(e: MouseEvent) {。。。} // 这里继承了MouseAdapter override fun mouseEntered(e: MouseEvent) {。。。} }
如上就是匿名对象的使用方式
3、下面的这个例子是实现接口的匿名对象
val person = object: Person { override fun printName() { subPrint() } private fun subPrint() { // 这里并非继承Person println("subPrint") } } person.printName() // person是一个对象,直接调用
标签:伴生,对象,kotlin,Companion,Person,User,nickname From: https://www.cnblogs.com/czwlinux/p/17780609.html