kotlin 伴生对象

发布时间 2023-10-30 09:41:55作者: LCAC

一、伴生对象的目标

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是一个对象,直接调用