I guess a "getting started" question although just...
# getting-started
c
I guess a "getting started" question although just shipped production full kotlin 😆 Will this be only evaluated once because
val
-ness, a sort of
lazy by
? Confused of the heuristics here. Should I make it
var
if I plan on it changing?
adapter
doesn't change, the
onActionPerformed
can
Copy code
val onActionPerformed: ((String, Any) -> Unit)?
        get() {
            return adapter?.onActionPerformed
        }
a
it will be evaluated every time it's accessed.
val
only means that there is no
set
operation available.
c
I see, thanks for the input! For some reason I equated
val
with
java final
from primitives
c
Think of Kotlin properties more as the getter and setter functions from Java, rather than the field itself. The fact that a field exists holding some value underneath it is really just an implementation detail. If it is
val
, it only has a getter function, no setter. And since the property is computed, there actually won’t be a field generated for that property. It’s literally just a getter function.
c
The questioning about it started to make me seem them more as getter/setter pairs, is that what properties are supposed to be? I am now confused about
val x = y.z()
vs
Copy code
val x: Z
   get() {
     return y.z()
   }
Are they the same thing?
val x = y.z()
feels to me like
final Object
assigned "on site" only, relying on some other
y
provided earlier in the constructor in the chain
So it's not really a pointer to a function but a reference to one instead?
The confusing part in heuristics is that the IDE tells me the
val
can't be reassigned if I write it like
val x = y.z()
and that implies some finality. Here y might not change, Z could. So my real problem is that yes I'm not reassigning the value, but the value does change 😆
Or at least that's what I want. So then doing the
get() {}
wrapper provides me with a runtime evaluation of it that
val x = y.z()
won't?
c
With
val x = y.z()
, you’re telling the compiler “when this class is created, compute
y.z()
and assign it to `x`“. There is no
get()
, so the compiler treats it like a
final Object
from Java, creates a backing field, and stores that value in that field. When you use
get()
, you’re telling the compiler “compute this value when it’s requested`, and being the smart compiler it is, it will omit the backing field since you’re declaring you want to re-compute it with each call
c
Thank you, so they are indeed different in implementations, great to know. saved me another release to fix 😆
That's exactly what I was thinking of. But then saying
val can't be reassigned
might not be a fully correct statement
val x = y.z()
is more
const
-ish
I was looking at
assigned
=>
change
As a 3rd party user of Kotlin separate of its dev, I see it might need more distinguishing, I assumed
val x = y.z()
if a getter would mean
val x = get{ y.z()}
, and many others might too
but it sounds like there's a more complex logic at play there. I understand it now, I'm just writing in case someone from the dev team sees and gets some ideas
c
Yeah, it is a bit confusing, especially with the semantics around
fun foo() = y.z()
being different than
val foo = y.z()
. But the main thing to keep in mind is that properties should be treated like getters and setters, and declaring
get()
or
set()
on them allows you to add custom computation to those getters and setters. Assignment still means what it normally does, setting some data somewhere, but whether its a field or something else is just an implementation detail
c
pseudocode
Copy code
if(return not wrapped in getter)
{ 
  x 
} else
{ 
   y 
}
Or maybe as a suggestion, have
const val
work as you said and normal
val
provide uniform functionality with
get{}
My concern with that is
val x = y.z()
, if treated as a
get()
, is not the assignment of a lambda that is called. Like you are not setting the getter, but you can, kinda, if you wrap it in
get(){.. }
val x = y.z()
is a getter of a sort of
const val temp = y.z()
evaluated before
x
, instead of
() -> y.z()
Love this discussion, Reminds me there's so many ways to look at things
I did semantics at my masters, got me excited 😄
My conclusion is: I'll use
Copy code
val x: Z
   get() {
     return y.z()
   }
instead of
val x = y.z()
because they are not the same thing, my
y.z()
might change even though
y
won't.
Yes, my biased implied syntactical sugar of turning the big version into the short version, figured it out 🙏 Started to even question vars, got down a weird rabbit hole
Also assumed the getters and setters could directly be set as lambdas
so the reference to the getter was
y.z()
@Casey Brooks Spamming tonight, srry not sorry, are you an insider? I'd add then
usually
to your statement of them being =
get + set
, because they act like fields if
val x = y.z()
doesn't indeed call
y.z()
and instead is the evaluation of it. It looks like
val/var
act as a Pair of immutable functions
Pair<Function<Unit,Type>,<Function<Type,Uni>t>
, yet with more than merely setting the getter/setter functions and why
val can't be reassigned
triggered some brain melt. Kotlin wrap what you give as assignments and getter like
Copy code
get() {
  return initialVal ?:
    getter()
}
, so you get different functionality if you wrap in getter. This sparks some more questions, why isn't assignment in
val
not allowed outside of
initialVal
if a val is not a final object and merely a var without the ability to set a setter.
I guess I see
val
logic contrasted to
var
abit odd
and this whole setter/getter assignment situation, the
val
having specific logic and rules. Sparked some great paths, love using Kotlin btw ❤️
From the docs
We can define custom accessors for a property. If we define a custom getter, it will be called every time we access the property (this allows us to implement a computed property). Here's an example of a custom getter:
Right. I went for oh if it is indeed only a getter, than assignment inline would mean assignment of the getter
c
A
val
without an explicit
get()
function is effectively equivalent to a
final
field in Java. Thus, you can set it a single time, at the object construction time, either through property initialization (
val x = y.z()
) or in the class’s
init { }
block.
Copy code
class Foo {
    // initialized in the class body
    val x: String = y.z()
}
Copy code
class Foo {
    // initialized in the class init block
    val x: String
    init {
        x = y.z()
    }
}
Because it’s
val
and not
var
, you are declaring that consumers can only ever access that property (
Foo().x
), but that they can never set a value to it. You can never do something like:
Copy code
val foo = Foo()
foo.x = y.z() // not allowed, because `x` is `val`
^ That is the meaning of
val
. You cannot reassign values to it. It just is what it is. OOP encapsulation states that you shouldn’t know why you can’t set that property, just that you can’t. Thus, as a consumer of that class, there is functionally no difference between
x
being a final property which cannot be reassigned, or a getter function that has no field to hold state. Compare this to the Java equivalents:
Copy code
// like `val x: String = y.z()`
public class Bar {
    private final String x = y.z();

    public String getX() {
        return x;
    }
}
Copy code
// like `val x: String get() { return y.z() }`
// also `val x: String get() = y.z()` (function `=` syntax)
public class Bar {
    public String getX() {
        return y.z();
    }
}
A user of this
Bar
class doesn’t know anything about
x
. The semantics of what’s going on underneath these are different, but the class’s public API is the exact same. All a consumer knows is there is a
getX()
function, but there’s a distinct lack of
setX(String x)
. A user of that class cannot set a value to
x
, but they are not able to know why without looking at the class’s internals. That’s all that Kotlin is saying with the
val
keyword, and literally what is generated by the compiler. There simply is no
setX()
function generated. Now, about the syntax of
val
itself: When you give an initial value to a property, the Kotlin compiler knows it needs to hold it somewhere, so it creates that backing field so it has somewhere to put it. But a user of the class is not able to know that: again, a user of the class just knows there is
getX()
as a method on the class (as seen from Java). When you do not have an initial value to give to the property, the compiler still needs a way to return a value from that
getX()
function it’s trying to generate. Thus, you’ll need to manually tell it the logic to stick in that function. But again, from the consumer of the class, there is no difference here, as it still just has that same
getX()
function (as seen from Java). So you have to provide a
get()
function and implement that logic like any other function, with a
return
statement inside a block, or using the
get() = y.x()
syntax shortcut. In this case, the
=
is on the
get()
function, not the property itself
c
Yes. We should be careful which words we use so we don't put people starting in the wrong place, like they are not effectively getters to begin with because of the presence of a possible separate state assigned only on initialization of the class. If anything they switch between their own getters internally if we were to phrase it like that, in case of how we provide the "getting". I was more focused on the DSL behind
val
to understand it better, I know setters and getters and pondered for some time their need in practice while doing java, I see their point fully, a REAL madman would access directly. I did a few projects sort of assuming the
val x = y.z()
is syntactic sugar for the
get(){}
implementation, oops 😆. Cheers for the detailed writeup. Although the OOP part about not questioning it, pfft, yeah sure, why not?
val
var
and their definition and heuristics give a story about the property that uses it, along with their assignment. It's important to me that a val returns a static value created on init or if it's something that's always compiled, that's exactly what my problem ended up being lol. That's how we get OOP2s and 3s, and how regimes rise or fall, etc.
c
There’s certainly some confusion with the choice of words here, but the Kotlin team definitely tries to make it known that
val
means “read-only”, not “immutable”, “static”, “constant” or anything like that. “read-only” is more appropriate to how it’s implemented, and is more consistent with the various ways
val
is used throughout the language in its various contexts. It also supports the weaker, more-general invariant of it in comparison to “final” or other stronger terms