Home > front end >  What difference does it make when we use get() in Kotlin
What difference does it make when we use get() in Kotlin

Time:09-18

I want to understand what is the difference at the deeper level between the two statements.

val myVariable: String get() = activity.myName
val myVariable: String = activity.myName

What difference does the get() makes even though I am able to access these variables from other classes and for me both works the same.

CodePudding user response:

It helps to understand it better if you move your get() = to the next line.

val myVariable: String 
    get() = activity.myName

Because then you'll see that you can also do this:

val myVariable: String = "something"
    get() = activity.myName

There are two things going on when you are defining a property.

  1. = "something" or = activity.myName right after the property name and type is the backing field initializer. When you include this, the property is given a backing field, which is an invisible variable that can hold data for the property to use, and the expression is used to define the initial value to hold in the backing field.

  2. Using get() creates a custom getter. This is a function that is run every time the property is accessed, and whatever the function returns is the value the property is read as. If you omit a custom getter, then it uses a default getter which simply returns the value of the backing field from point 1.

You cannot omit both the field and the custom getter, because then you would have a default getter with no backing field to retrieve, which makes no sense.

So now we can explain three possible ways of writing this property:

val myProperty: String 
    get() = activity.myName
// Every time you use this property, it looks up the activity property and finds the
// value of the activity's myName property. So if activity was changed to point to
// something else or myName was changed to point to something else, myProperty will
// always return the latest value when it is used.

val myProperty: String = activity.myName
// This property is initialized with the value that activity.myName holds at the time
// the class is instantiated, and it will always return this same original value even
// if activity or myName changes, because the value of the backing field is never changed.
// The value of the backing field can only be changed if you're using a var property.

val myProperty: String = activity.myName
    get() = activity.myName
// This property behaves like the first one above, but it also has a useless backing
// field that is holding a reference to the original value of activity.myName. Since
// the custom getter doesn't use the backing field, the backing field can never be
// read, so it's useless.

Here's one possible use case for using both a backing field and a custom getter. If you want to keep a constant value in a backing field like in the second example above, but have some side effect like logging something each time it is accessed, you could use both. The keyword field can be used in a custom getter to access the backing field.

val myProperty: String = activity.myName
    get() = field.also { Log.i(TAG, "Retrieved value of myProperty.") }

CodePudding user response:

To add onto Tenfour04's answer, another way you could write your get() version is:

val myVariable: String get() {
    return activity.myName
}

And that's clearly a function, right? You call it, it returns a String. When a function is simple and returns a value, you can format it as a single-expression function which looks like this:

// block function
fun something(): String {
    return coolString
}

// single-expression function - you can omit the type too, if you like
fun something(): String = coolString

So the single-expression equivalent for your getter is

val myVariable: String get() = return activity.myName

The fact it's a val changes things - there are special getter and setter functions you can add to vals and vars, and you're sort of "tacking on" the function at the end of the variable declaration. Because it's a val, it's a property - you read it like a value, like println(myVariable). It's not a function, so you don't call it like println(myVariable()) or anything.

But in this case, because it has this getter function, under the hood Kotlin is calling that function whenever you read the value. And really, anything can happen in there! The value could be completely different every time. Maybe there's no value stored at all, and the getter just calls some other function to generate a value. But the code reading myVariable doesn't know or care about that - it just accesses the variable, and sees a value.

They're basically equivalent to the Java-style getter and setter functions:

fun getMyVariable(): String {
    // or whatever you want to return
    return activity.myName
}

fun setMyVariable(value: String) {
    // do whatever with the value - store it in a variable, record it in some database, etc
}

Except you don't need to treat them as special and call a function to access them - they look and act just like a plain old variable! (And in fact, if you're calling Java setters and getters named like this, Kotlin will let you access them like properties)


So yeah, both the versions in the original question can be accessed the same way - that's the idea! But one is a basic variable - a val in fact, so it's a fixed value. The other is a getter which can do anything, including returning different values over time, calculating stuff on demand, or even just pointing at other values - maybe you want to give access to the other variable, especially if it's a var, or maybe in some versions of a class property A is identical to property B so it's easier to keep one updated and let the other just say "whatever that one says"

  • Related