3

由于 Kotlin 不允许您在 Java 中的classlikestatic成员中直接声明常量,因此我们有几个选项来说明如何在 Kotlin 中做到这一点。我的问题是,每个选项的性能成本是多少,哪个更便宜?我不想知道哪个更具可读性或被认为是最佳实践,我只想从性能的角度来看它。

选项 1:使用伴随对象

所以一个看起来像这样的代码:

class Thing {
    companion object {
        const val TAG = "Thing"
    }

   ...
}

在 Java 中看起来像这样:

public final class Thing {
   public static final String TAG = "Thing";
   public static final Thing.Companion Companion = new Thing.Companion((DefaultConstructorMarker)null);

   public static final class Companion {
      private Companion() {
      }

      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }

   ...
}

它创建这个内部类Companion并实例化它。这是多么昂贵?

选项 2:将其声明为顶级变量

所以这:

const val TAG = "Thing"
class Thing {
   ...
}

变成这样:

public final class ThingKt {
   public static final String TAG = "Thing";
}

public final class Thing { ... }

它创建一个带有static成员的新类。这与使用对象存储它有什么不同?

选项 3:使用外部对象

像这样:

object ThingConstants {
    const val TAG = "Thing"
}

class Thing { ... }

在 Java 中看起来像这样:

public final class ThingConstants {
   public static final String TAG = "Thing";
   public static final ThingConstants INSTANCE;

   ...  // Private constructor
}

public final class Thing { ... }

类似于声明为顶级,除了创建的类有一个INSTANCE在私有构造函数中初始化的字段。与使用顶级变量相比,这有多昂贵?

选项 4:删除 const 修饰符

因此,您只需使用旧的val

class Thing {
    val TAG = "Thing"
    
    ...
}

这对我来说是个谜。它不会创建任何额外的类,但也不会获得const修饰符的好处。这意味着该类的每个实例都将拥有自己的 实例TAG,并为其生成 getter 和 setter,对吗?但我也读到JVM 将其优化为常量。所以这个选项比我最初想象的要多。

4

1 回答 1

2

选项 1-3 在性能方面是相同的,因为它们都是内联的const,因此它们在编译时使用的任何地方都是内联的。

选项 4 不会为类的每个实例创建 String 的重复实例。相反,该类的每个实例都有一个指向同一个共享实例的成员字段。就性能而言,从技术上讲,每次检索它时都必须调用 getter 方法,但我希望现代 VM 能够在运行时优化该开销。

选项 1-3 确实创建了额外的 Java 类定义,因此它们的前期内存开销更大。但是,如果您在这些对象或顶级文件中已经有其他成员,那么无论如何您已经拥有这些类。选项 4 在类中有一个额外的字段,因此如果该类有很多实例,它的权重可能会更大。

于 2021-12-30T04:54:39.430 回答