Kotlin伴随对象的初始化方法示例讲解

作者:昉钰 时间:2022-07-24 05:53:06 

在Java中我们知道静态变量会在类加载时机的&ldquo;初始化&rdquo;阶段得到赋值(编译器会收集类中的静态变量及静态代码块,然后在类构造方法<clinit>()中执行,注意:这里不是实例构造方法),也就是真正运行程序中的代码;执行完类构造方法之后才会执行我们熟悉的实例构造方法。

  而在Kotlin中有所谓的伴随对象,用过的同学都知道,它的功能类似于Java中的静态变量,那它又是什么时候初始化的呢?来看一个例子,代码如下:

package com.zfang.testapp
class KConstructTest(val first: String, val second: Int) {
   init {// 111
       println("KConstructTest init")
       test(1)
   }
   companion object CC {
       init { // 222
           println("companion object init")
       }
       fun test(index: Int) {
           println("test, index = $index")
       }
   }
}

一个简单的kotlin类,里面包含一个伴随对象CC,现在写一个测试类来看一个标记111和222这两个地方谁先初始化,测试代码如下:

package com.zfang.testapp;
class Test {
   public static void main(String[] args) {
       KConstructTest test = new KConstructTest("ttt", 1);
       KConstructTest.CC cc = test.CC;
   }
}

一个简单的Java测试类,入口中直接new了一个KConstructTest对象,下面是程序输出:

companion object init
KConstructTest init
test, index = 1

从输出结果中可以看出是伴随对象的init代码块先执行了,然后才是主类中的init代码块执行。下面我们反编译看下生存的java类是怎样的。结果如下:

package com.zfang.testapp;
import kotlin.Metadata;
import kotlin.jvm.internal.DefaultConstructorMarker;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
@Metadata(
  mv = {1, 7, 1},
  k = 1,
  d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0002\b\u0007\u0018\u0000 \u000b2\u00020\u0001:\u0001\u000bB\u0015\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0002\u0010\u0006R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u0007\u0010\bR\u0011\u0010\u0004\u001a\u00020\u0005¢\u0006\b\n\u0000\u001a\u0004\b\t\u0010\n¨\u0006\f"},
  d2 = {"Lcom/zfang/testapp/KConstructTest;", "", "first", "", "second", "", "(Ljava/lang/String;I)V", "getFirst", "()Ljava/lang/String;", "getSecond", "()I", "CC", "app_debug"}
)
public final class KConstructTest {
  @NotNull
  private final String first;
  private final int second;
  @NotNull
  public static final KConstructTest.CC CC = new KConstructTest.CC((DefaultConstructorMarker)null);
  @NotNull
  public final String getFirst() {
     return this.first;
  }
  public final int getSecond() {
     return this.second;
  }
  public KConstructTest(@NotNull String first, int second) {//与主构造方法对应
     Intrinsics.checkNotNullParameter(first, "first");
     super();
     this.first = first;
     this.second = second;
     String var3 = "KConstructTest init";//主类中的init代码块
     System.out.println(var3);
     CC.test(1);
  }
  static {//伴随对象中的init代码块
     String var0 = "companion object init";
     System.out.println(var0);
  }
  @Metadata(
     mv = {1, 7, 1},
     k = 1,
     d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\b\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u000e\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u0006¨\u0006\u0007"},
     d2 = {"Lcom/zfang/testapp/KConstructTest$CC;", "", "()V", "test", "", "index", "", "app_debug"}
  )
  public static final class CC {//伴随对象类
     public final void test(int index) {
        String var2 = "test, index = " + index;
        System.out.println(var2);
     }
     private CC() {
     }
     // $FF: synthetic method
     public CC(DefaultConstructorMarker $constructor_marker) {
        this();
     }
  }
}

额,反编译出来的Java代码看上去有点多的样子。不过逻辑还是很简单的,主要以下几点:

  • 伴随对象类编译成了与Java相对应的静态内部类(又叫内嵌类),并且伴随对象中的init代码块编译成了主类中的静态代码块。

  • 主类primary构造函数相应的就是Java类的构造函数,同时主类中的init代码块则编译到了主构造函数中了。

根据以上分析则可以得出结论:

  • 伴随对象中的init代码块首先执行(因为它被编译成主要的静态代码块了,在类的初始化阶段就会执行)。

  • 然后才会执行主类中的Init代码块(此代码块被编译到相应Java代码中的实例构造方法里面了,执行完类构造方法之后才会执行实例构造方法)。

所以如果项目中对主类Init块和伴随对象init块有初始化顺序要求的就需要注意相应的逻辑了。

来源:https://blog.csdn.net/www586089/article/details/127983595

标签:Kotlin,伴随对象,初始化
0
投稿

猜你喜欢

  • Android蓝牙开发深入解析

    2022-09-21 12:49:03
  • 详解Java中如何正确书写单例模式

    2022-06-20 01:35:00
  • winform实现限制及解除鼠标移动范围的方法

    2023-07-17 22:11:51
  • elasticsearch集群cluster示例详解

    2023-12-11 16:49:27
  • c#实现一元二次方程求解器示例分享

    2023-01-03 21:15:30
  • Spring boot创建自定义starter的完整步骤

    2023-05-08 08:08:53
  • java生成饼图svg及JFreeChart生成svg图表

    2023-10-25 19:27:00
  • spring boot使用自定义的线程池执行Async任务

    2023-08-15 07:41:25
  • SpringCloud升级2020.0.x版之OpenFeign简介与使用实现思路

    2021-10-02 08:30:56
  • ListView实现聊天列表之处理不同数据项

    2022-07-01 01:06:58
  • c语言实现可自定义的游戏地图

    2021-10-25 14:38:34
  • 使用JAXBContext 设置xml节点属性

    2021-09-21 00:05:44
  • 详解Mybatis中的PooledDataSource

    2022-10-24 18:08:11
  • Java类加载器ClassLoader用法解析

    2023-05-05 21:46:38
  • Java实现简单树结构

    2023-08-06 18:59:12
  • Java爬虫范例之使用Htmlunit爬取学校教务网课程表信息

    2021-07-17 00:52:51
  • 基于Java生成GUID的实现方法

    2022-04-09 02:44:09
  • Android实现简单卡片布局

    2023-05-22 11:43:37
  • Android仿抖音列表效果

    2022-03-01 16:36:50
  • Android从0到完整项目(1)使用Android studio 创建项目详解

    2022-12-13 13:48:21
  • asp之家 软件编程 m.aspxhome.com