Constants in Kotlin: Stop Guessing, Start Using Them Correctly
\n\nIf you've been writing in Kotlin for at least a week, you've almost certainly encountered the question: "How do I properly declare a constant?" It seems simple enough — there's val, there's const val, there's companion object. But the devil, as always, is in the details. And this question on Stack Overflow has gathered thousands of views for a reason.
Let's finally figure out what ways of creating constants exist in Kotlin, when to use which, and why val is not always a constant. Let's go!
1. val vs const val: What's the Catch?
\n\nThe most common question from beginners: "I wrote val — isn't that a constant?" The answer: yes and no.
val is an immutable reference (read-only). But the value is computed at runtime. That means it's not a compile-time constant.
// This is NOT a compile-time constant\nval currentTime = System.currentTimeMillis()\n\nconst val is a real constant that is inlined into the code at compile time (like static final in Java).
// But this IS a constant\nconst val PI = 3.14159\n\nImportant limitation: const val can only be used with primitive types and String. No objects, lists, or custom classes.
💡 Golden rule: If the value is known at compile time — use const val. If it's computed at runtime — use val.
2. Where to Declare Constants? Top 3 Ways
\n\nWay 1: Top-level Constants (Recommended)
\n\nThe simplest and cleanest way is to declare a constant at the top level of a file. No classes, no objects — just a file with constants.
\n\n// Constants.kt\nconst val BASE_URL = "https://api.example.com"\nconst val TIMEOUT_SECONDS = 30\nconst val APP_NAME = "MyKotlinApp"\n\nWhy is this great?
\n- \n
- Minimal code \n
- Accessible from everywhere without prefixes \n
- The compiler inlines the values \n
- Easy to refactor \n
Way 2: companion object Inside a Class
\n\nA classic approach from Java that migrated to Kotlin. Use it when constants are logically related to a class.
\n\nclass NetworkClient {\n companion object {\n const val DEFAULT_PORT = 8080\n const val PROTOCOL = "https"\n }\n}\n\n// Usage\nval port = NetworkClient.DEFAULT_PORT\n\nDownside: syntactic noise. Upside: constants are tied to a context.
\n\nWay 3: object (Singleton) for Groups of Constants
\n\nWhen there are many constants and they need to be grouped by meaning.
\n\nobject AppConstants {\n const val MAX_LOGIN_ATTEMPTS = 5\n const val SESSION_DURATION = 3600L\n const val DATE_FORMAT = "yyyy-MM-dd"\n\n object Urls {\n const val LOGIN = "/auth/login"\n const val LOGOUT = "/auth/logout"\n }\n}\n\n// Usage\nAppConstants.Urls.LOGIN\n\n3. Constants in Android: Specifics
\n\nIf you're writing for Android, there are a few additional nuances.
\n\nConstants in companion object and @JvmStatic
\n\nTo make a constant from a companion object a true static in Java (e.g., for annotations), use @JvmStatic:
class MyFragment : Fragment() {\n companion object {\n @JvmStatic\n const val ARG_ID = "id"\n }\n}\n\nConstants in Resources
\n\nDon't forget the classic Android way — resources. For strings, colors, dimensions:
\n\n// strings.xml\n<string name="app_name">MyApp</string>\n\n// In code\nval appName = getString(R.string.app_name)\n\nBut these are not compile-time constants.