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

 更新时间:2022年11月28日 09:12:48   作者:昉钰  
Kotlin语言的许多特性,包括变量不可变,变量不可为空,等等。这些特性都是为了尽可能地保证程序安全而设计的,但是有些时候这些特性也会在编码时给我们带来不少的麻烦,下面我们来了解伴随对象的初始化

在Java中我们知道静态变量会在类加载时机的“初始化”阶段得到赋值(编译器会收集类中的静态变量及静态代码块,然后在类构造方法<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块有初始化顺序要求的就需要注意相应的逻辑了。

到此这篇关于Kotlin伴随对象的初始化方法示例讲解的文章就介绍到这了,更多相关Kotlin伴随对象的初始化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Android 屏幕双击事件的捕获简单示例

    Android 屏幕双击事件的捕获简单示例

    本文主要介绍 Android屏幕双击事件的捕获,这里整理了相关资料,并附示例代码,有兴趣的小伙伴可以参考下
    2016-08-08
  • Android实现在TextView文字过长时省略部分或滚动显示的方法

    Android实现在TextView文字过长时省略部分或滚动显示的方法

    这篇文章主要介绍了Android实现在TextView文字过长时省略部分或滚动显示的方法,结合实例形式分析了Android中TextView控件文字显示及滚动效果相关操作技巧,需要的朋友可以参考下
    2016-10-10
  • Android使用fragment实现左侧导航

    Android使用fragment实现左侧导航

    这篇文章主要为大家详细介绍了Android使用fragment实现左侧导航,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • Flutter学习之构建、布局及绘制三部曲

    Flutter学习之构建、布局及绘制三部曲

    这篇文章主要给大家介绍了关于Flutter学习之构建、布局及绘制三部曲的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Flutter具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04
  • 官网项目Jetpack Startup库学习

    官网项目Jetpack Startup库学习

    这篇文章主要为大家介绍了官网项目Jetpack Startup库学习,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Android RecyclerView添加FootView和HeadView

    Android RecyclerView添加FootView和HeadView

    这篇文章主要介绍了Android RecyclerView添加FootView和HeadView的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • 浅谈Android Classloader动态加载分析

    浅谈Android Classloader动态加载分析

    这篇文章主要介绍了浅谈Android Classloader动态加载分析,详细的介绍了ClassLoader概念、分类,具有一定的参考价值,有兴趣的可以了解一下
    2018-03-03
  • Android自定义Animation实现View摇摆效果

    Android自定义Animation实现View摇摆效果

    这篇文章主要为大家详细介绍了Android自定义Animation实现View摇摆效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Android开发仿扫一扫实现拍摄框内的照片功能

    Android开发仿扫一扫实现拍摄框内的照片功能

    无论是微信还是支付宝扫一扫功能很常用,那么它基于代码是如何实现的呢?今天小编给大家分享android开发之仿扫一扫实现拍摄框内的照片功能,感兴趣的朋友一起学习吧
    2016-09-09
  • 最常见的猜拳小游戏Android代码实现

    最常见的猜拳小游戏Android代码实现

    这篇文章主要为大家详细介绍了最常见的猜拳小游戏Android代码实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-08-08

最新评论