There are situations where
a) One don’t want to have default values for a class, since it contains some properties, which are other classes, where it does not make sense has an initial value. It would be a unuseful task.
b) It is uncomfortable to declare all properties types as nullable, because it demands use !!
operator for all useful calculations.
c) It is not comfortable to assign the entire class at once, because it comes from, for example, reading from a SQLite database, where you retrieve the fields one by one, using a cursor
In this case, there is no convenient solution because lateinit
only resolves when the instance assignment of a class is complete.
Therefore
class Cl (
var b1: Byte,
var b2: Byte
)
fun main () {
lateinit var c: Cl
c.b1 = 5 // (*)
println (c.b1)
}
It gives no compiler error, but it gives a nasty runtime error. UninitializedPropertyAccessException: lateinit property c has not been initialized
. It’s the same runtime error if you use c.b1
in right side of an assignement. It’s not fair.
I think that Kotlin It should consider statetement c.b1=5
correct but the class yet not initialized.
Read the guide, the error is correct.
For the SQLite stuff you are right, but solutions like the one you recommended does not solve the problem, indeed they make it worse.
Have a look at Ktorm or Exposed for SQL DB related solutions (NB: since those are JDBC based solutions they do not work on Android)
1 Like
I know that it is the correct behavour,
So I’ve posted the text as a suggestion for Kotlin
. I think it would be more comfortable for programmers. It’s obvious that left side assignment is not a real use for the field, it’s like a partial assignment. Maybe for allowing this proposed feature, it should be create a new feature to warn the runtime that this instance is fully assigned
I’m not using the class as a true OOP, I’m using more as a record (like C), with access to properties, to construct more complex objects.
class C(
b1:byte,
b2:byte
)
c:C
c.b1 = 5
// There are situations (like cursors) where each field is assigned separately
c.b2 = 10
c.done()
It would be an equivalent of
c:C(5,10)
I’ve edited my previous message.
You should be right, but, for now, (just for practical reasons) it is simpler for me set default values for the class properties, including properties that is linked with another class. My app is not SQLite centric, and I don’t want download extra softwares for just some queries.
So, schematically, I will do:
class Class2(
dado1:Byte,
dado2:Byte)
vClass2 = Class2(3,3)
class C(
b1: Byte=0,
b2: Class2=vClass2
)
vC:C
vC.b1 = 12
....
vC.b2=Class2(1,1)
If you need a variables dispenser you can go for something like:
interface MyVariableDispenser {
var myVar1: MyCustomType1
var myVar2: MyCustomType2
}
class MyVariablesDispenserImpl(
private val container: MutableMap = HashMap()
) : MyVariableDispenser {
var myVar1: MyCustomType1 by container
var myVar2: MyCustomType2 by container
}
Of course you can avoid the interface, but it is more clean this way. Another way could be:
interface MyVariableDispenser {
var myVar1: MyCustomType1
var myVar2: MyCustomType2
}
object MyDispenser : MyVariableDispenser {
private val container = HashMap()
var myVar1: MyCustomType1 by container
var myVar2: MyCustomType2 by container
}
fun main() {
MyDispenser.myVar1 = MyCustomType1()
val retrieved = MyDispenser.myVar1
}