Question
Java static final Equivalent in Kotlin: Constants with const val and Companion Objects
Question
In Java, a constant is often declared like this:
class Hello {
public static final int MAX_LEN = 20;
}
What is the Kotlin equivalent of this pattern?
More specifically:
- How do you declare a constant in Kotlin?
- When should you use
const valversusval? - Where should the constant be placed: top-level, inside an
object, or inside acompanion object?
Short Answer
By the end of this page, you will understand how Kotlin represents constants that are similar to Java static final fields. You will learn when to use const val, when a plain val is enough, and where constants are typically declared in Kotlin code such as top-level files, object declarations, and companion objects.
Concept
In Java, static final is commonly used for constants:
public static final int MAX_LEN = 20;
This combines two ideas:
static: the value belongs to the class, not to an instancefinal: the value cannot be reassigned
Kotlin does not have the static keyword in the same way Java does. Instead, Kotlin offers several ways to define values that behave like class-level constants.
The most direct equivalent for a compile-time constant is:
const val MAX_LEN = 20
A const val means:
- it is read-only
- its value is known at compile time
- it can be used in places that require a compile-time constant
Examples of allowed const val values include:
- strings
- numbers
- booleans
- chars
If a value is read-only but not known at compile time, use val instead:
val appName = getAppNameFromConfig()
That is not a compile-time constant, so it cannot be const.
In real Kotlin code, constants are commonly placed in one of these locations:
- Top-level: ideal for general file-level constants
- Inside an
object: useful for grouping related constants - Inside a
companion object: useful when the constant conceptually belongs to a class
So the Kotlin equivalent depends on the exact use case, but the core idea is usually const val.
Mental Model
Think of Java static final as a value pinned to the wall of a room:
- static means everyone in the room shares the same pinned note
- final means nobody can replace the note
In Kotlin, you still pin the note in one shared place, but the language gives you different walls to pin it on:
- a file wall: top-level
const val - a category wall: inside an
object - a class-related wall: inside a
companion object
If the note must be written before the program starts and never change, use const val. If it is still read-only but created later, use val.
Syntax and Examples
The most common Kotlin forms are:
1. Top-level constant
const val MAX_LEN = 20
Use this when the constant does not need to belong to a specific class.
2. Inside an object
object Limits {
const val MAX_LEN = 20
}
Usage:
println(Limits.MAX_LEN)
This is useful for grouping related constants.
3. Inside a companion object
class Hello {
companion object {
const val MAX_LEN = 20
}
}
Usage:
println(Hello.MAX_LEN)
This is the closest style to a Java class constant.
4. Read-only but not compile-time constant
class Hello {
companion object {
val maxLen = listOf(10, 20).max()
}
}
This must be val, not const val, because the value is computed.
Beginner-friendly example
class UserValidator {
companion object {
const val MIN_USERNAME_LENGTH = 3
}
fun isValid(username: String): Boolean {
return username.length >= MIN_USERNAME_LENGTH
}
}
fun main() {
val validator = UserValidator()
println(validator.isValid("Al"))
println(validator.isValid("Alice"))
println(UserValidator.MIN_USERNAME_LENGTH)
}
Explanation:
MIN_USERNAME_LENGTHbelongs to the class, not each validator instance- it never changes
- its value is known at compile time
companion objectlets you access it asUserValidator.MIN_USERNAME_LENGTH
Step by Step Execution
Consider this example:
class Hello {
companion object {
const val MAX_LEN = 20
}
}
fun main() {
println(Hello.MAX_LEN)
}
Step by step:
class Hellodefines a class.- Inside it,
companion objectcreates a shared object associated with the class. const val MAX_LEN = 20defines a constant inside that companion object.- Because it is in the companion object, Kotlin lets you access it using the class name:
Hello.MAX_LEN. main()callsprintln(Hello.MAX_LEN).- The output is:
20
Now compare with a top-level constant:
const val MAX_LEN = 20
fun main() {
println(MAX_LEN)
}
What happens here:
MAX_LENis declared outside any class.- It becomes a shared constant for the file/package.
main()can use it directly.- The output is still
20.
The main difference is organization: top-level constants are not attached to a class.
Real World Use Cases
Constants like Java static final fields appear everywhere in Kotlin applications.
Common examples
- API paths
- error codes
- configuration keys
- default limits
- screen route names
- database column names
- intent/action names in Android
Example: API configuration
object ApiConfig {
const val BASE_URL = "https://api.example.com"
const val TIMEOUT_SECONDS = 30
}
Example: validation rules
class PasswordValidator {
companion object {
const val MIN_LENGTH = 8
}
}
Example: keys used across a project
object PreferencesKeys {
const val USER_TOKEN = "user_token"
const val THEME = "theme"
}
These constants make code easier to maintain because the value is defined once and reused everywhere.
Real Codebase Usage
In real Kotlin projects, developers usually choose the location of a constant based on meaning and scope.
Common patterns
Top-level constants
Used when the constant is shared and does not belong to one class.
const val DEFAULT_PAGE_SIZE = 20
This is common in utility files or package-level configuration.
Grouping with object
Used to organize related constants.
object HttpHeaders {
const val AUTHORIZATION = "Authorization"
const val CONTENT_TYPE = "Content-Type"
}
This avoids scattering unrelated constants across files.
Companion object for class-related values
Used when the constant logically belongs to a class.
class CacheManager {
companion object {
const val DEFAULT_CACHE_SIZE = 100
}
}
Validation and guard clauses
Constants are often used in checks.
const val MIN_AGE = 18
fun register(age: Int) {
require(age >= MIN_AGE) { "Age must be at least $MIN_AGE" }
}
Error handling and message reuse
object ErrorMessages {
const val NETWORK_ERROR = "Network request failed"
}
Using constants avoids duplicated string literals and reduces mistakes.
Common Mistakes
Here are some common beginner mistakes when translating Java static final to Kotlin.
1. Using const with a non-constant expression
Broken code:
const val MAX_LEN = listOf(10, 20).max()
Why it fails:
const valrequires a compile-time constant- this value is computed at runtime
Correct version:
val maxLen = listOf(10, 20).max()
2. Expecting val to be the same as const val
val name = "Kotlin"
This is read-only, but not necessarily a compile-time constant in all contexts.
Use const val when the value is truly constant and allowed by Kotlin:
const val NAME = "Kotlin"
3. Looking for the static keyword
Broken expectation:
class Hello {
static final val MAX_LEN = 20
}
Why it fails:
- Kotlin does not use
staticlike Java
Correct version:
class Hello {
companion object {
const val MAX_LEN = 20
}
}
4. Putting every constant inside a class unnecessarily
This works, but may be poor organization if the constant is not class-specific.
Better alternative:
const val DEFAULT_TIMEOUT = 30
Use top-level constants when no class ownership is needed.
5. Using mutable variables for fixed values
Broken code:
var MAX_LEN = 20
Why it is a problem:
varcan be reassigned- constants should not change
Correct version:
const val MAX_LEN = 20
6. Assuming const val works with any type
Broken code:
data class Config(val size: Int)
const val DEFAULT_CONFIG = Config(10)
Why it fails:
- custom objects are not compile-time constants
Correct version:
data class Config(val size: Int)
val DEFAULT_CONFIG = Config(10)
Comparisons
Here is a quick comparison of Kotlin options.
| Kotlin form | Mutable? | Compile-time constant? | Best use case |
|---|---|---|---|
const val | No | Yes | True constants like fixed strings and numbers |
val | No | No, not necessarily | Read-only values computed at runtime |
var | Yes | No | Values that must change |
Placement comparison
| Location | Example | Use when |
|---|---|---|
| Top-level | const val MAX_LEN = 20 | The constant is general and not tied to a class |
object | object Limits { const val MAX_LEN = 20 } | You want to group related constants |
companion object | class Hello { companion object { const val MAX_LEN = 20 } } | The constant logically belongs to a class |
Java vs Kotlin
| Java | Kotlin |
|---|---|
public static final int MAX_LEN = 20; | const val MAX_LEN = 20 or companion object { const val MAX_LEN = 20 } |
Uses static for class-level members | Uses top-level declarations, object, or companion object |
Uses final for read-only fields | Uses val for read-only, const val for compile-time constants |
Cheat Sheet
// Top-level compile-time constant
const val MAX_LEN = 20
// Grouped constants
object Limits {
const val MAX_LEN = 20
}
// Class-related constant
class Hello {
companion object {
const val MAX_LEN = 20
}
}
// Read-only runtime value
val maxLen = listOf(10, 20).max()
Rules
- Use
const valfor compile-time constants only. - Use
valfor read-only values computed at runtime. - Use
varonly when reassignment is needed. - Kotlin does not use Java-style
static. - Put constants at top level if they do not belong to a specific class.
- Use
objectto group related constants. - Use
companion objectif the constant belongs to a class.
Common valid const val types
StringIntLongDoubleFloatBooleanChar
Not allowed with const val
- values returned by functions
- lists, maps, and custom objects
- runtime calculations that are not compile-time constants
FAQ
What is the Kotlin equivalent of Java static final?
Usually const val for true constants, often placed at top level or inside a companion object.
Should I use const val or val in Kotlin?
Use const val if the value is known at compile time. Use val if it is read-only but computed at runtime.
Does Kotlin have static fields?
Not in the Java syntax. Kotlin uses top-level declarations, object, and companion object instead.
Can I declare a constant inside a class in Kotlin?
Yes. Put it inside a companion object if you want class-style access like MyClass.MY_CONSTANT.
Can const val hold a custom object?
No. const val only works for compile-time constant values such as strings, numbers, booleans, and chars.
Is top-level const val better than companion object?
Not always. Use top-level for general constants and companion object when the constant logically belongs to a class.
Why not just use var for constants?
Because var allows reassignment. Constants should be read-only, so use const val or val.
Mini Project
Description
Build a small Kotlin validation utility for user registration. The project demonstrates how to declare constants in idiomatic Kotlin using top-level constants, grouped constants in an object, and class-related constants in a companion object.
Goal
Create a simple program that validates username and password input using Kotlin constants declared in the most appropriate places.
Requirements
- Create at least one top-level constant for a general application rule.
- Create an
objectthat groups related text keys or messages. - Create a class with a
companion objectconstant used during validation. - Write a function that checks whether a username and password are valid.
- Print clear results for at least two test users.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.