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.