Consider this Kotlin code:
var parent: T? = null
get() = if (isParent) this as T else field
set(value) { field = if (value == null) null else value.parent }
val isParent: Boolean
get() = parent == null
var description = ""
get() = if (isParent) field else parent!!.description
set(value) { if (isParent) field = value else parent!!.description = value }
Assume that isParent returns true if this instance is a parent instance. If not getParent() will return the parent instance. In Java you are allowed to access directly field of a different instance of same class like this:
String getDescription() { return getParent().description; }
void setDescription(String value) { getParent().description = value; }
(I am not saying that is a best thing to do, I simplified it for demostration). Comparing to Java, it would be nice to be able to do following:
var description = ""
get() = parent.field
set(value) { parent.field = value }
However this does not work and unfortunately it makes the code less readable. Especially if you have a lot of such variables, which are bound to this parent.
A backing field of a property can only be accessed from a getter or setter of that property, and only for the instance on which the getter or setter has been invoked. If you need to provide multiple ways to access an attribute of a class, you need to define two distinct properties, one of which has a backing field to store the data and another has a getter and setter referring to the first property.
class Foo {
var parent: Foo? = null
val parentOrSelf: Foo get() = parent ?: this
private var _description: String? = null
var description = ""
get() = parentOrSelf._description
set(value) { parentOrSelf._description = value }
}
Related
in the below code I am trying to create a getter method as a backing field.
so that, when the getLastNameLen property is invoked it should return the length of the lastNameset.
please refer to the code below and help me to fix the bug.
how to display output of the backing fields
code:
class Thomas (val nickname: String?, val age : Int?) {
//backing field 1
var lastName : String? = null
set(value) {
if (value?.length == 0) throw IllegalArgumentException("negative values are not allowed")
field = value
println("lastname backing field set: ${field} ")
}
val getLastNameLen
get() = {
this.lastName?.length
}
}
output
lastname backing field set: jr.stephan
lastName is jr.stephan
lastNameLen is () -> kotlin.Int?
This is because you are using the = operator which is setting the getter to be a lambda.
You have two options:
val getLastNameLen
get() {
return this.lastName?.length
}
OR
val getLastNameLen
get() = this.lastName?.length
basically use the brackets right after get() to make a getter function, or if you can do it in one line use an = right after the get() but don't include the {} otherwise it will treat it like its a lambda
I want a Kotlin property that can replace the original value. For example:
var String.setString
get() = "ignorethis"
set(value) = this = value
(I'm actually using it on UBytes to add getters and setters for the upper and lower nibbles).
However, in my example above I get Variable expected. Is it possible to modify this with an extension property?
String is immutable, and you can only do that on mutable types like this:
fun main() {
val x = mutableListOf("old")
x.setString = mutableListOf("New Pro Max Extra Large Plus")
println(x)
}
var <T> MutableList<T>.setString
get() = this
set(value) = value.forEachIndexed { idx, it -> this[idx] = it } ```
Given a Kotlin data class, how do you initialize a non-nullable property as a pointer to self? I.e something like the following pseudocode.
data class Node(var other: Node = this)
Currently I have a solution that introduces temporary properties
data class Node(val _other: Node? = null) {
var other: Node = _other ?: this
}
This is not possible. You cannot access this before it is constructed. But that's just how the default constructor parameter would work.
Cannot access '<this>' before superclass constructor has been called
Thank you for your feedback, however, for my purposes I need the functionality of a data class like equals and copy and I would prefer not to have make the property nullable and/or implementing the functionality manually.
You'd still have to: equals and copy will care only about _other and ignore other (just as they would ignore all other properties defined in the body of the class). That other is a var just makes it worse: reassigning it will have no effect on data class functionality.
But you can come closer:
data class Node(private var _other: Node? = null) {
var other: Node
get() = _other ?: this
set(value) {
_other = if (value != this) value else null
}
}
The only problem remaining is that component1() will return _other. In this case you have a single property so it shouldn't matter.
EDIT: after thinking a bit more,
data class Node(private var _other: Node? = null) {
init {
this._other = _other ?: this
}
var other: Node
get() = _other!! // safe
set(value) {
_other = value
}
}
seems to effectively be what you want. You can see difference here:
val node1 = Node()
val node2 = node1.copy(node1)
println(node1 == node2)
prints false with the first solution, true with the second one (as it should if this was the default parameter).
I have an abstract class containing the following property
var items: List<I> = listOf()
set(value) {
field = value
onDataChanged()
}
In my extending class i now want to override the setter of items to do additional stuff before the above setter code is called. Is this possible, if yes, how?
You have to declare your field as open in your parent class
open var items: List<I> = listOf()
set(value) {
field = value
onDataChanged()
}
And in your child class you override it as:
override var items: List<Int>
get() = super.items
set(value) {
super.items = value
//Your code
}
In this way, you are actually creating a property without a backing field and you are just accessing to the real parent's items field.
I'm new to Kotlin, so I have this interface.
interface User {
var nickName : String
}
Now I want to create a class PrivateUser that implements this interface. I have also to implement the abstract member nickName.
Via constructor it's very simple
class PrivateUser(override var nickName: String) : User
However when I try to implement member inside the class Idea generates me this code
class Button: User {
override var nickName: String
get() = TODO("not implemented")
set(value) {}
}
It's confusing to me how to implement it further.
Properties must be initialized in Kotlin. When you declare the property in the constructor, it gets initialized with whatever you pass in. If you declare it in the body, you need to define it yourself, either with a default value, or parsed from other properties.
Some examples:
class Button : User {
override var nickname = "Fred"
}
class Button(val firstName: String, val lastName: String) : User {
override var nickname = "${firstname[0]}$lastname"
}
The code generated by IDEA is useful if you want a non-default getter and/or setter, or if you want a property without a backing field (it's getter and setter calculate on the fly when accessed).
More examples:
class Button : User {
override var nickname = "Fred"
get() = if (field.isEmpty()) "N/A" else field
set(value) {
// No Tommy
field = if (value == "Tommy") "" else value
}
}
class Button(val number: Int) : User {
var id = "$number"
private set
override var nickname: String
get() {
val parts = id.split('-')
return if (parts.size > 1) parts[0] else ""
}
set(value) {
field = if (value.isEmpty()) "$number" else "$value-$number"
}
}