Kotlin 中的静态方法与伴生对象:深入解析

与 Java 不同,Kotlin 不支持类的静态方法。大多数读者都知道,静态方法不属于对象实例,而是属于类型本身。在 Kotlin 中,建议我们在包级别定义方法,以实现静态方法的功能。让我们创建一个新的 Kotlin 文件并将其命名为 Static。在这个文件中,我们将编写一个函数的代码,该函数将返回输入字符串的第一个字符(如果输入为空,将抛出异常),代码如下:

fun showFirstCharacter(input:String):Char{
  if(input.isEmpty()) throw IllegalArgumentException()
  return input.first()
}

然后,在我们的代码中,我们可以直接调用 INLINECODEc5a25d50("Kotlin is cool!")。编译器在这里替我们完成了一些工作。使用 INLINECODE43681438,我们可以查看生成的字节码。只需运行 javap -c StaticKt.class 即可获得编译器生成的代码:

Compiled from "Static.kt"
public final class com.gfg.kotlin.StaticKt {
 public static final char showFirstCharacter(java.lang.String);
 Code:
 0: aload_0
 1: ldc #9 //String input
 3: invokestatic #15 //Method
kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
 ...
 40: aload_0
 41: checkcast #17 //class java/lang/CharSequence
 44: invokestatic #35 //Method
kotlin/text/StringsKt.first:(Ljava/lang/CharSequence;)C
 47: ireturn
}

正如大家从输出中看到的,编译器实际上为我们生成了一个类,并将其标记为 INLINECODE94664ede;如大家所知,它是不可被继承的。在这个类中,编译器添加了我们定义的函数。让我们从程序入口点调用这个方法,再次使用 INLINECODE94a473fb 工具,我们可以看看字节码是什么样的:

fun main(args: Array) {
println("First letter:" + showFirstCharacter("Kotlin is cool")
}
Compiled from "gfg.kt"

public final class com.gfg.kotlin.gfgkt {
public static final void main(java.lang.String[]);
Code:
0: aload_0
...
18: ldc #29 //String Kotlin is cool
20: invokestatic #35 //Method
com/folder/kotlin.StaticKt.showFirstCharacter:(Ljava/lang/String;)C
}

为了简洁起见,大部分字节码被省略了,但在第 20 行大家可以看到对我们方法的调用;具体来说,这个调用是通过 invokestatic 例程进行的。

我们在讨论静态方法时,不能不提单例模式。单例是一种设计模式,它将给定类的实例化限制为一个实例。一旦创建,它将在程序的整个生命周期中存在。Kotlin 借鉴了 Scala 中的方法。以下是在 Kotlin 中定义单例的方法:

object Singleton{
  private var count = 0
  fun doSomething():Unit {
    println("Calling a doSomething (${++count} call/-s in total)")
  }
 }

现在,我们可以从任何函数调用 Singleton.doSomething,每次都会看到计数器增加。如果你查看生成的字节码,你会发现编译器再次为我们完成了一些工作:

public final class com.gfg.kotlin.Singleton {
 public static final com.gfg.kotlin.Singleton INSTANCE;
 public final void doSomething();
 Code:
 0: new #10 // class java/lang/StringBuilder
 43: return
 ...
 static {};
 Code:
 0: new #2 //class
com/gfg/kotlin/Singleton
 3: invokespecial #61 //Method "":()V
 6: return
}

我们省略了为 INLINECODE3597438f 方法生成的代码,因为它不是本文的重点。编译器再次创建了一个类并将其标记为 INLINECODEe80d5918。此外,它引入了一个名为 INLINECODE6f930997 的成员并将其标记为 INLINECODE4584ac53。有趣的部分在于列表的末尾,大家可以看到 static {}; 条目。这是类初始化器,它只被调用一次,JVM 会确保这种情况发生在以下操作之前:

  • 创建类的实例
  • 调用类的静态方法
  • 赋值类的静态字段
  • 使用非常量静态字段
  • 执行词法嵌套在类中的 top-level 类的断言语句

在这种情况下,代码在第一次调用 INLINECODE1b7b5027 之前被调用,因为我们访问了静态成员 INLINECODE34c64011(请参见下面的 getstatic 字节码例程)。如果我们调用这个方法两次,我们将得到以下字节码:

public static final void main(java.lang.String[]);
Code:
0: aload_0
1: ldc #9 // String args
3: invokestatic #15 //Method
kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
6: getstatic #21 //Field
com/gfg/kotlin/Singleton.INSTANCE:Lcom/gfg/kotlin/Singleton;
9: invokevirtual #25 //Method
com/gfg/kotlin/Singleton.doSomething:()V
12: getstatic #21 //Field
com/gfg/kotlin/Singleton.INSTANCE:Lcom/gfg/kotlin/Singleton;
15: invokevirtual #25 //Method
com/gfg/kotlin/Singleton.doSomething:()V
18: return

大家可以看到,在这两种情况下,INLINECODE5f2dfa9d 都是通过虚方法调用(INLINECODEb2e2772a)执行的,并且是针对同一个 INSTANCE 进行的。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/43950.html
点赞
0.00 平均评分 (0% 分数) - 0