Explain Codes LogoExplain Codes Logo

What is the equivalent of Java static final fields in Kotlin?

java
kotlin
constants
javainteroperability
Anton ShumikhinbyAnton Shumikhin·Aug 14, 2024
⚡TLDR

In Kotlin, you substitute Java's static final with a companion object. The const val construct is used for compile-time constants such as primitives or String, whereas for other types, you'd simply use val.

For Primitive/String:

class MyClass { companion object { const val CONSTANT: String = "Value" // Kotlin lingo for everlasting love! } }

For Other types:

class MyClass { companion object { val CONSTANT = OtherType() // Locke: What is constant in life? Kotlin: This! } }

Access using MyClass.CONSTANT, similar to how static final in Java would be accessed.

Deep dive into Kotlin constants

Comprehensive guide to declaring constants

In Kotlin, you use const val before a primitive or String type to declare a compile-time constant. Note that const val hints the compiler that the value will be a constant expression:

const val MAX_LEN = 100 // Like the age of a ninja turtle...nearly immutable 🐢

If you need to access this constant throughout your application, define it within a companion object or an object declaration:

object Resource { const val NAME = "MyResource" // Shhh! Top secret resource alias }

Interoperability with Java: @JvmStatic and @JvmField

We love const val but sometimes we need to play nice with Java. This is where @JvmStatic and @JvmField annotations enter the scene. @JvmStatic before a val in a Companion Object exposes it as a static field to Java:

class MyClass { companion object { @JvmStatic val STATIC_VAL = "StaticValue" // Yes, even Kotlin can do a static shock ⚡ } }

And for a property to be a static field (no getters here!) visible to Java, use @JvmField:

class MyClass { companion object { @JvmField val FIELD_VAL = OtherType() // "Fielding" questions in Java! } }

Scope the constants: Within file, Class or globally?

Using const val you can define compile-time constants. However, read-only field that are to be initialized during runtime (and aren't necessarily a compile-time constant) you use private val, and the visibility will be limited to the scope of the file:

private val FileLevelValue = computeExpensiveOperation() // Only for elite file-level eyes 👀

Note, const val should be used for primitives and String types. Use it within an object, companion object, or at the top level of a file for global constants.

Naming constants: Uppercase with underscores

Just like Java, in Kotlin too constants are often defined in uppercase letters with underscores separating different words:

const val MAX_SIZE = 100 // Because MAX_SIZE matters. Always. 💾

Uppercase naming differentiates compile-time constants from mutable variables or read-only values:

private val cacheSize = MAX_SIZE * 2 // Double the trouble! But read-only, so chill!

Advanced usage of Kotlin constants

Best practices: When and how to use constants

If you're dealing with true constants that are known and can't change at compile-time, use const val:

const val MAX_BUFFER_SIZE = 1000 // Trust me, you don't want any bigger 📦

For values that need to be computed at runtime but will remain constant thereafter, use val:

companion object { val BUFFER_SIZE = computeOptimalBufferSize() // I compute, therefore I am 💻 }

Lastly, always group your constants logically using objects or companion objects. This way you provide order and improve readability.

Potential pitfalls to avoid

Here are some common issues that may arise:

  • Erroneous references: Make sure to access constants statically - ClassName.CONSTANT, and not through instance references.
  • Initialization order: Keep initialization order in mind when dealing with companion objects.
  • Java interoperability: Don't forget to use @JvmStatic or @JvmField when Kotlin properties need to be accessed as static from Java.