为什么Kotlin中var变量可在Lambda表达式中正常使用?
var Variables in Lambdas (Unlike Java's Effectively Final Rule) Great question! This is one of those thoughtful Kotlin features that removes tedious Java boilerplate, so let’s break down exactly what’s happening under the hood.
First, a Quick Recap of Java’s Rule
In Java, if you want to use a variable inside a lambda (or anonymous inner class), that variable has to be effectively final—either explicitly marked final, or never modified after initialization. This is because Java captures the direct value of the variable. If the variable could change, the lambda would have no reliable way to access the updated value without breaking consistency.
Kotlin’s Sneaky Workaround: Wrapping Variables
Kotlin doesn’t force you to use effectively final variables in lambdas because it automatically wraps mutable var variables in a hidden reference container. Here’s what the compiler does behind the scenes with your code:
- It takes your mutable
var stringBuilderand wraps it in a simple container class (think of it like this):private class Ref<T>(var value: T) - Instead of a direct
var, you get an effectively final reference to this container:val stringBuilderRef = Ref(StringBuilder()) - Every time you assign to
stringBuilder(likestringBuilder = StringBuilder()), the compiler translates that to updating the container’svalueproperty:stringBuilderRef.value = StringBuilder() - When your lambda accesses
stringBuilder.toString(), it’s actually pulling the currentvaluefrom the container:Toast.makeText(this, stringBuilderRef.value.toString(), ...)
Since the container reference (stringBuilderRef) is effectively final (never reassigned), it complies with the JVM’s underlying requirements for lambda captures. But because we’re modifying the content of the container, the lambda always gets the latest value of your original var.
Why Your Specific Code Works
In your example:
- You first assign
stringBuilderto aStringBuilderwith "Old" - Then you reassign it to a new
StringBuilderwith "New" - The lambda captures the hidden container, so when the button is clicked, it reads the current
valueof the container—the new "New" StringBuilder.
A Quick Note on Thread Safety
While this feature is super convenient, keep in mind: if you’re modifying the var from one thread and accessing it in a lambda running on another thread, you might run into visibility issues or race conditions. For multi-threaded scenarios, you should still use thread-safe constructs (like AtomicReference) to ensure consistency.
内容的提问来源于stack exchange,提问作者Maksim Dmitriev

