# Val vs Var in Kotlin: Immutable and Mutable Properties Explained for Android Developers

> Master Kotlin val vs var for Android. Learn when to use immutable val or mutable var properties. Prefer val for safer, thread-safe code and boost your app development.

- Repository: [JetBrains/kotlin](https://github.com/jetbrains/kotlin)
- Tags: deep-dive
- Published: 2026-02-16

---

**Use `val` for read-only properties that never change after initialization, and `var` for mutable properties that need reassignment; prefer `val` by default for safer, thread-safe Android code.**

In the JetBrains/kotlin repository, the distinction between `val` and `var` is enforced at every level of the compiler pipeline—from lexical tokens to bytecode generation. Understanding this core difference is essential for writing robust Android applications that leverage Kotlin's immutability guarantees.

## What Is the Difference Between Val and Var in Kotlin?

Kotlin uses two distinct keywords to control mutability at the property level. The compiler treats these as fundamentally different constructs, generating different bytecode and enforcing different constraints.

### Val: Read-Only References

A `val` declares a **read-only** property. You can assign it a value exactly once—either at declaration time, in an `init` block, or inside a constructor. After initialization, the reference cannot be reassigned.

Under the hood, the Kotlin compiler creates a `PropertyDescriptorImpl` with `isVar = false` (see [`core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java`](https://github.com/JetBrains/kotlin/blob/main/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java) line 68). At the JVM level, this translates to a `final` field with no setter generated.

### Var: Mutable References

A `var` declares a **mutable** property. You can reassign its value any number of times after the initial assignment. The compiler generates both a getter and a setter for the property.

In the descriptor system, `var` properties are flagged with `isVar = true` (same file, line 68). The JVM backend generates a non-final backing field and a setter method, allowing runtime reassignment.

## How Kotlin Implements Val vs Var at the Compiler Level

The distinction between `val` and `var` is not merely syntactic sugar; it propagates through every phase of the Kotlin compiler.

### Lexical Analysis and Tokenization

During the initial lexing phase, the Kotlin compiler identifies property declarations using distinct tokens. In [`compiler/multiplatform-parsing/common/src/org/jetbrains/kotlin/kmp/lexer/KtTokens.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/multiplatform-parsing/common/src/org/jetbrains/kotlin/kmp/lexer/KtTokens.kt), line 18 defines `VAL_KEYWORD` for `val`, while line 19 defines `VAR_KEYWORD` for `var`. These tokens determine how the parser constructs the property's abstract syntax tree.

### Descriptor and Symbol Resolution

When the compiler builds property descriptors for semantic analysis, it instantiates `PropertyDescriptorImpl` with the `isVar` boolean flag. This flag persists through the Analysis API, where `KaPropertySymbol.isVal` reflects the original declaration choice. The renderer in [`analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/renderer/declarations/renderers/callables/KaKotlinPropertySymbolRenderer.kt`](https://github.com/JetBrains/kotlin/blob/main/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/renderer/declarations/renderers/callables/KaKotlinPropertySymbolRenderer.kt) (line 35) selects `KtTokens.VAL_KEYWORD` or `VAR_KEYWORD` based on this flag when displaying property signatures.

### Bytecode Generation

At the backend, the `isVar` flag determines JVM bytecode output:
- **For `val`**: The backing field is marked `final`, and no setter is generated.
- **For `var`**: The backing field is non-final, and the compiler generates a setter method.

This guarantees that the immutability contract is enforced at the JVM level, not just at compile-time.

## Val vs Var in Android Development: Best Practices

Android applications benefit significantly from Kotlin's immutability model. The Android framework's lifecycle and threading model make the choice between `val` and `var` particularly important.

### When to Use Val in Android

Prefer `val` as your default choice for properties. It communicates intent clearly and eliminates an entire class of threading bugs.

**Compile-time constants**: Use `const val` for configuration keys, request codes, and API endpoints that never change.

```kotlin
const val REQUEST_CODE_CAMERA = 1001
const val BASE_URL = "https://api.example.com/"

```

**Dependency injection**: Constructor-injected dependencies should always be `val` since they never change after the object is created.

```kotlin
class UserRepository @Inject constructor(
    private val api: UserApi,
    private val dao: UserDao
)

```

**Immutable UI state**: When using `StateFlow` or `LiveData`, declare the flow itself as `val` while mutating the internal value.

```kotlin
class MainViewModel : ViewModel() {
    // The reference never changes - val
    val uiState: StateFlow<UiState> = MutableStateFlow(UiState.Initial)
    
    fun setLoading(isLoading: Boolean) {
        // Mutating the value inside the flow, not the reference
        (uiState as MutableStateFlow).value = uiState.value.copy(isLoading = isLoading)
    }
}

```

### When to Use Var in Android

Use `var` only when the property's value must change after initialization.

**Mutable UI counters and flags**: Fragment or Activity-level state that updates during user interaction.

```kotlin
class CounterFragment : Fragment(R.layout.fragment_counter) {
    private var clickCount = 0  // Must increment, so var required
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        view.findViewById<Button>(R.id.button).setOnClickListener {
            clickCount++
            updateUI()
        }
    }
}

```

**Late-initialized properties**: Android's view binding and dependency injection often require `lateinit var` because the value is set after object construction but before use.

```kotlin
class DetailsFragment : Fragment() {
    private lateinit var binding: FragmentDetailsBinding  // Set in onCreateView
    
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        binding = FragmentDetailsBinding.inflate(inflater, container, false)
        return binding.root
    }
}

```

**Mutable collections**: When you need to add or remove elements from a collection held as a property.

```kotlin
class TaskManager {
    private var tasks = mutableListOf<Task>()  // List contents change, reference might too
    
    fun addTask(task: Task) {
        tasks.add(task)
    }
}

```

## Thread Safety and Performance Implications

The choice between `val` and `var` directly impacts your Android app's stability and performance.

**Thread safety**: Because a `val` property's reference never changes, it is inherently thread-safe to share across coroutines, background threads, and the main thread. The object itself might have mutable state, but the reference stability eliminates race conditions during reassignment. In contrast, reassigning a `var` from multiple threads without synchronization leads to non-deterministic behavior and potential memory visibility issues.

**Performance**: On the JVM, `val` properties compile to `final` fields. The JIT compiler can inline `final` fields more aggressively, reducing indirection and improving cache locality. `var` properties require generated setter methods and non-final fields, adding slight runtime overhead and preventing certain optimizations.

## Summary

- **`val`** creates a read-only property with a single assignment; the compiler generates a `final` field and no setter, making it thread-safe by default.
- **`var`** creates a mutable property allowing multiple reassignments; the compiler generates a setter and marks the field as non-final.
- **Default to `val`** in Android projects for dependency injection, UI state holders, and constants to leverage immutability guarantees.
- **Use `var`** only for counters, late-initialized bindings, and collections that must change after construction.
- The distinction is enforced at the compiler level through `PropertyDescriptorImpl.isVar`, lexer tokens in [`KtTokens.kt`](https://github.com/JetBrains/kotlin/blob/main/KtTokens.kt), and JVM bytecode generation.

## Frequently Asked Questions

### Can I change the value of a val property in Kotlin?

No, you cannot reassign a `val` property after its initial assignment. The compiler enforces this at the semantic analysis phase by setting `isVar = false` in the property descriptor. However, if the `val` holds a reference to a mutable object (like a `MutableList`), the internal state of that object can change even though the reference itself remains constant.

### Why does Kotlin recommend using val by default?

Kotlin encourages `val` by default to promote immutability, which reduces bugs related to state changes and makes code easier to reason about. According to the Kotlin compiler source in [`PropertyDescriptorImpl.java`](https://github.com/JetBrains/kotlin/blob/main/PropertyDescriptorImpl.java), `val` properties generate `final` fields on the JVM, which are inherently thread-safe for reference sharing and allow the JIT compiler to perform additional optimizations like inlining.

### When should I use lateinit var instead of val in Android?

Use `lateinit var` when you must declare a property at class construction time but cannot initialize it until later in the Android lifecycle, such as in `onCreate()` or `onCreateView()`. This is common for View Binding, where the view hierarchy doesn't exist when the Fragment is instantiated. Note that `lateinit` requires `var` because the property must be reassigned once when initialized, and accessing it before initialization throws an `UninitializedPropertyAccessException`.

### Is there a performance difference between val and var on Android?

Yes, there is a subtle performance advantage to `val`. Because `val` compiles to a `final` field on the JVM, the JIT compiler can inline the value at call sites and optimize memory layout more aggressively. `var` properties require generated getter and setter methods (unless private) and non-final fields, introducing slight method call overhead and preventing certain optimizations. For high-frequency operations on Android's main thread, preferring `val` can contribute to smoother frame rates.