1

The following kotlin code val nameHash get() = name.hashCode() can be compiled into java as follows

public final int getNameHash() {
    return name.hashCode();
}

and the property nameHash disapears. However when the val is changed to var, the compiler says "Property must be initialized" What is the deeper difference between var and val?

Kevin Paul
  • 11
  • 2

3 Answers3

5

How does kotlin compiler know whether a val should be a property or a function

As far as the Kotlin language is concerned, val denotes properties, never functions. However, there is a difference between these two property declarations:

val nameHash get() = name.hashCode()
var nameHash get() = name.hashCode()

And that is that the first property does not have a backing field. Properties with backing fields must be initialised one way or another, for example:

var nameHash = 0 // for example
    get() = name.hashCode()

And this is why your code with var didn't compile.

If you are asking for the situations when a backing field is generated for a Kotlin property, they are listed in the spec:

However, the backing field is created for a property only in the following cases

  • A property has no custom accessors;
  • A property has a default accessor;
  • A property has a custom accessor, and it uses field property;
  • A mutable property has a custom getter or setter, but not both.

These are the cases where your property needs a backing field. Your var nameHash satisfies that last case, because it is a "mutable property". If you use val instead, it is not a mutable property anymore and doesn't satisfy any of those cases.

Intuitively, a mutable property without setter needs a backing field because one must need to be able to set the property. How can you set it when it has no setter? Well, the Kotlin compiler solves the problem by generating a backing field and just sets the backing field instead.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
0

Property is a functions set() & get(). Read-only properties implement only the get() function, but still, it's a function, so everything written in the property will be executed every time it's called.

In Kotlin, keywords: val is the same as the read-only property, meaning it's required to implement only get() function. When you put var keyword, compiler expects you to implement both get() & set() functions.

So, compile error there because your property missing set() function that is usually needed to store a value (or as the compiler says: must be initialized).

Oleg
  • 591
  • 5
  • 14
  • 1
    You don't need to provide a custom setter (or getter!) - if you omit either, it'll just read or write to the backing field as normal: https://pl.kotl.in/AQCsKNklU But it *does* always need an initial value for that field (and you can't initialise it during construction or with `lateinit` unfortunately - always gotta assign *something* even if it'll be immediately discarded) – cactustictacs Jul 02 '22 at 18:20
0

The error message is a little confusing in this case. The difference between val and var is that val means there is a getter while var means there is a getter and a setter. To fix your code you need to add an implementation for the setter:

var nameHash
    get() = name.hashCode()
    set(hash: Int) { ... }

Although, in this case I don't think it makes too much sense. We can't set the hash code value of the name.

broot
  • 21,588
  • 3
  • 30
  • 35
  • Nah it's like Sweeper says - a `var` creates a backing field (unless you create a custom getter *and* setter and neither of them reference `field`) so you have to assign that an initial value, that's what the error message is about. If you don't create a custom getter or setter it'll just use the default one that reads or writes to the backing field (which is basically what a plain `var` declaration does) – cactustictacs Jul 02 '22 at 18:29
  • 1
    @cactustictacs Yes, I understand what's happening. I mean that from the perspective of someone who first created `val` with a custom getter and without using a backing field, when they now switch to `var`, it probably doesn't make sense to fix the error by initializing the property. Even the compiler detects such solution as "suspicious". What they need instead is to add a custom setter with the logic similar to the getter. Which is actually impossible in the OP's case. – broot Jul 02 '22 at 19:31
  • Yeah I just wanted to be more explicit about what's happening (just for anyone reading) that there's a backing field involved, and that's why there's an error about initialisation. So you don't *necessarily* need to create a setter (but if you have a getter that doesn't use the field, you probably want a setter that doesn't either) - and in the OP's case, it's more of a sign that maybe they don't want a `var` at all, like you were saying – cactustictacs Jul 02 '22 at 21:05
  • Yeah, I think I'll just remove my answer to not confuse others :-) – broot Jul 02 '22 at 21:12
  • I think it's useful since it points out you can write that setter that doesn't use `field` instead! I mean that's typically what you'd do if your getter doesn't use a backing field. It's just using the OP's example with the hashcode makes it less clear since you can't write a setter for that really – cactustictacs Jul 02 '22 at 21:18