What is deferred assignment in Kotlin? - kotlin

In Kotlin Documentation, there is mentioned about deferred assignment
val a: Int = 1 // immediate assignment
val b = 2 // `Int` type is inferred
val c: Int // Type required when no initializer is provided
c = 3 // **deferred assignment**
What is the meaning of deferred assignment?

This means simply that the variable is initialized not in its declaration but at a later point.

As stated in the Documentation the val c: Int is declared before c is initialized in the following line.
This means you can initialize a non nullable val inside a function some lines after you declared it.
For example like this:
class Hichhiker {
fun foo() {
val firstName: String
val lastName: String
val age: Int
firstName = "Arthur"
lastName = "Dent"
age = 40
}
}
Instead of this:
class Hichhiker {
fun foo() {
val firstName = "Arthur"
val lastName = "Dent"
val age = 40
}
}
So the compiler recognizes the initalization of the non nullable val even if the assignment was deferred by some lines of code.
For something similar on the class level and more often used see lateinit and delegates like lazy()

It simply means that the variable has been initialized and the assignment of a value to it takes place lines of code below the initialization.Meaning the assignment has been postponed for later.

Related

Kotlin property initialization

I've created the following snippet, not anything that I would actually write in production, but just to help me understand how properties are initialized:
class C {
val a: String = run {
println("init a")
f()
}
val b: String = run {
println("init b: a=$a")
a
}
private fun f(): String {
println("f(): a=$a, b=$b")
return b
}
}
fun main() {
println(C().a)
println(C().b)
}
Note that all properties are val and String, so immutable and non-nullable. Yet the output shows that they are null.
Is the result of this code well-defined? Is there somewhere in the language spec that says that a non-nullable property can in fact be null when you access it from the wrong place?
According to the Kotlin/Core spec, the order of the execution is specified,
When a classifier type is initialized using a particular secondary constructor ctor delegated to primary constructor pctor which, in turn, is delegated to the corresponding superclass constructor sctor , the following happens, in this initialization order:
[...]
Each property initialization code as well as the initialization blocks in the class body are invoked in the order of appearance in the
class body;
[...]
but the values of the uninitialised properties are not.
If any of the properties are accessed before they are initialized w.r.t initialization order (e.g., if a method called in an initialization block accesses a property declared after the initialization block), the value of the property is unspecified. It stays unspecified even after the “proper” initialization is performed.
In other words, you will see the messages printed in the order of
init a
f(): a=null, b=null
init b: a=null
null
but the values are not guaranteed to be null.
There might be stronger guarantees in Kotlin/JVM, but there is no spec for that right now.
This is explicitly not an answer, just two small remarks/questions:
First, doesn't one of the three members a, b, or f() need to be set to a or return a value? The way it is in the OPs code, everything is null by definition as there is no non-null value anywhere. The 'call chain' is a –> f() –> b, so if we set b to a value like "start", it would look like this:
class C {
val a: String = run { f() }
val b: String = run { "start" }
private fun f(): String = b
}
fun main() {
val c = C()
println(c.a) // Output: null
println(c.b) // Output: "start"
}
Secondly, if the order is changed so that b is appearing in the code before the other two members we get a different result:
class C {
val b: String = run { "start" }
val a: String = run { f() }
private fun f(): String = b
}
fun main() {
val c = C()
println(c.a) // Output: "start"
println(c.b) // Output: "start"
}

Why is it not possible to use the constructor parameter (not property) directly as variables for a member function?

Following example will describe my misunderstanding the best I think:
class myExampleClass (
myString: String,
val myInt: Int,
) {
fun memberFunction() {
val memberFunctionValA = myString // does not work
val memberFunctionValB = myInt // does work
}
}
Is there a specific reason? Do we always have to declare parameters as properties to use them inside the class?
For declaring properties and initializing them from the primary constructor, Kotlin has a concise syntax:
class Person(val firstName: String, val lastName: String, var age: Int) { /*...*/ }
I found this on https://kotlinlang.org/docs/reference/classes.html
As far as I can understand you missed a val keyword in the first parameter.
class myExampleClass (
val myString: String, // this might work
val myInt: Int,
) {
fun memberFunction() {
val memberFunctionValA = myString // does not work
val memberFunctionValB = myInt // does work
}
}

Can I set an object variable with a reflection value to a property of another object?

I have a class that has a property whose type is KMutableVar1. The objects of that class have a variable assigned to a reflection of another class's property. I have a function that is supposed take in an object of the first class and uses its variable of type KMutableVar1 to determine which property of an object of the second class to edit.
jeez that paragraph is awful im so so sorry ><
I have already tried assigning the object's KMutableVar1 variable to another variable and then trying to tie that variable to an object using dot notation, but that variable name isn't in the primary constructor for the class and thus an error occurs.
class Thing(var amount: Int, var id: Int){
fun editAttributes(object: Thing, editor: RemoteEdit){
//My initial thought here was to do the following:
var editing = editor.attributeToEdit
object.editing = editor.newValue
//But this raises an error since class 'thing' has no attribute 'editing'
}
}
var bananas = Thing(amount = 12, id = 21)
class RemoteEdit(var attributeToEdit: KMutableVar1, var newValue: Int)
var remoteEditor = RemoteEdit(attributeToEdit = Thing::amount, newValue = 23)
My intent is for the function to change bananas.amount to 23.
Sorry, I'm not fully understanding why you need it but it will work I guess:
import kotlin.reflect.KMutableProperty1
class Thing(var amount: Int, var id: Int) {
fun editAttributes(editor: RemoteEdit) {
val editing = editor.attributeToEdit
editing.set(this, editor.newValue)
}
}
class RemoteEdit(var attributeToEdit: KMutableProperty1<Thing, Int>, var newValue: Int)
fun main() {
val bananas = Thing(amount = 12, id = 21)
val remoteEditor = RemoteEdit(attributeToEdit = Thing::amount, newValue = 23)
bananas.editAttributes(remoteEditor)
println(bananas.amount) // prints 23
}
I have a feeling this might be an XY problem because there are so many unusual things going on in your code. Why would the implementation of changing the property value through reflection be in the class that's being edited?
I suppose if there is some reason you need to be able to pass these parameters for editing around, you would need a class, but then it makes sense for it to implement the function for using it by itself:
class RemoteEdit<T, R>(var attributeToEdit: KMutableProperty1<T, R>, var newValue: R) {
fun execute(item: T) {
attributeToEdit.set(item, newValue)
}
}
val bananas = Thing(amount = 12, id = 21)
val edit23 = RemoteEdit(Thing::amount, 23)
edit23.execute(bananas)
If you don't need to pass these around, all you need is a top level function:
fun <T, R> editProperty(item: T, attributeToEdit: KMutableProperty1<T, R>, newValue: R) =
attributeToEdit.set(item, newValue)
val bananas = Thing(amount = 12, id = 21)
editProperty(bananas, Thing::amount, 23)

Same property name and primary constructor parameter name in Kotlin

I'm little confused how kotlin is managing the property name and the primary constructor parameter name. If I wrote the same property name and the parameter name then kotlin compiler gives an error.
class Student(name : String, roll : Int){
val name: String
init {
name = "Asif"
}
}
It gives this error.
> Error:(9, 5) Kotlin: Property must be initialized or be abstract
> Error:(12, 9) Kotlin: Val cannot be reassigned
But when I change the name of the property val name : String or the changing the name of the parameter of the primary constructor name : String then the code will work and compile.
This will work or compile fine.
class Student(pName : String, roll : Int){
val name: String
init {
name = "Asif"
}
}
What is the reason behind this? Why we can't have the same primary constructor's parameter name and the property name?
Primary constructor parameters are available in property initializers and initializer blocks (this is what makes the primary constructor special).
In your init block, name refers to the constructor parameter, which as all other function parameters, cannot be reassigned. This is the second error. The first one is for the same reason, now your property isn't initialized anywhere.
If you want to initialize your property, you can still refer to it as this.name:
class Student(name : String, roll : Int){
val name: String
init {
this.name = "Asif"
}
}
Init block provides parameters from the default constructor. To assign name field of your object, you have to explicitly use this.name:
class Student(name : String, roll : Int){
val name: String
init {
this.name = "Asif"
}
}
But more kotlin-way is to declare field with default value directly in the constructor:
class Student(val name : String = "DefaultName", roll : Int){ }
The answer was provided by others but to clarify look at these versions:
(a)
class Student1(name : String, roll : Int){
val name: String
init {
this.name = "Asif"
}
}
(b)
class Student2(name : String, roll : Int){
val name: String = "Asif"
}
(c)
class Student3(val name : String = "DefaultName", roll : Int)
Classes Student1 and Student2 are totally equivalent but Student3 is not: If you run the below code:
val s = Student1("Nick", 2)
println(s.name)
val s2 = Student2("Nick", 2)
println(s2.name)
val s3 = Student3("Nick", 2)
println(s3.name)
you will see:
Asif
Asif
Nick
Kotlin provides concise and easy way like below:
class Student(var name : String, roll : Int){
init {
name = "Asif"
}
}
Copied: In fact, for declaring properties and initializing them from the primary constructor, Kotlin has a concise syntax. Refer here

Can a field be cast to non null version of itself?

I have a data class
data class MyModel(private val _data: MyData? = null)
And I want to ensure my data is only accessible when it is not null, else throw.
I use the below which is good.
fun getData(): MyData {
return checkNotNull(_data) { "data shouldn't be null" }
}
However, if I follow the guide as per Override getter for Kotlin data class, the below complaints I need to return MyData? instead of MyData
val data = _data
get(): MyData {
return checkNotNull(field) { "data shouldn't be null" }
}
Is it true that field can't be cast to the Non-null version of it when return?
If your goal is to declare a getter for a Any? property that returns a Any, it's not possible. You'll get the following error:
Getter return type must be equal to the type of the property
So attempting to do something like
val test : String?
get() : String = "hi"
Wouldn't work.
However, you could hide the nullable property and expose a non-nullable property which references the nullable value via casting:
private val test : String? = "hi"
val testNotNull : String = test as String
If test referenced null, an exception will be thrown.
For example:
fun main(args: Array<String>) = print(Demo().testNotNull)
class Demo(private var test: String? = "hi") {
val testNotNull : String
. get() = test as String
}
You can test this snippit out at try.kotlin.org
Although this is not safe. You should rethink your design. If you're not interoping with Java, you shouldn't punish yourself with nullable types.
I don’t think you can. What you did with the fun getData() is a valid approach IMO. Or you could just not use a data class and create a normal class, obviously.
What I think it may work is with something like this:
typealias notNullType = MyData
data class Test(private val _value: MyData? = null) {
val v: notNullType = _value as notNullType
get() { return field }
}
This would totally allow you to do:
fun play() {
val t = Test(null)
print(t.v) //see what I did? :-)
}
THAT BEING SAID… I don’t think “hiding” the ? optional is necessarily a good idea.
It doesn't necessarily mean that the MyData class is null if you cast it like MyData?
The '?' Just allows the object to be null in the instance that it actually becomes null to avoid an exception at runtime.
You can make your class nullable and it can still contain your data.