I have been looking at Kotlin official tutorial. I came across the topic called Backing Fields
It says,
Classes in Kotlin cannot have fields. However, sometimes it is necessary to have a backing field when using custom accessors. For these purposes, Kotlin provides an automatic backing field which can be accessed using the field identifier:
var counter = 0 // the initializer value is written directly to the backing field
set(value) {
if (value >= 0) field = value
}
I got the above from this official link
My question is, is the "field" pointing to counter variable ?
Can someone please provide me an example for the backing field or describe me in an understanding word ?
Consider this class
class SomeClass {
var counter: Int = 0
set(value) {
if (value >= 0) field = value
}
}
In Android Studio go to Main menu -> Tools -> Kotlin -> Show Kotlin Bytecode and click Decompile in the Kotlin bytecode panel.
What you see is the equivalent code in Java.
public final class SomeClass {
private int counter;
public final int getCounter() {
return this.counter;
}
public final void setCounter(int value) {
if(value >= 0) {
this.counter = value;
}
}
}
The field keyword allows you to assign a value inside a custom setter. In kotlin counter = 3 will call set(3). So if you would define
var counter=0
set(value){
counter = value
}
It would recursively call itself until your stack is full and your process crashes.
The field keyword assigns the value directly without calling the setter again.
A Backing Field is just a field that will be generated for a property
in a class only if it uses the default implementation of at least one
of the accessors
Backing field is generated only if a property uses the default implementation of getter/setter. If you see the following code with perspective of Java. It looks correct. However in "kotlin" it’ll throw Exception.
class User{
var firstName : String //backing field generated
get() = firstName
set(value) {
firstName = value
}
var lastName : String //backing field generated
get() = lastName
set(value) {
lastName = value
}
val name : String //no backing field generated
get() = "{$firstName $lastName}"
var address : String = "XYZ" //^because there is no default //^implementation of an accessor
}
In Kotlin the above code snippet will throw StackOverflow because when we access or set property "first-name" or "last name" the default accessor will be called. So in Kotlin "user.firstName = "value"” is same as Java’s "user.setFirstName("value")".
So when "set(value) {firstName = "value"} " is called, then a recursive callhappens and compiler throws a Exception exception because we are calling setter inside the setter.
Solution to this problem is to user backing fields. In Kotlin a backing field can be accessed using "field" keyword inside accessors. Take a look at corrected code snippet below.
class User{
var firstName : String get() = field
set(value) {
field = value
}
var lastName : String get() = field
set(value) {
field = value}
}
}
How it works , let's understand by an example , consider this
class Person {
var name: String = ""
}
If nothing is specified, the property(name) uses the default getter and setter. It can, of course,
be modified to run whatever custom behaviour you need, without having to change
the existing code:
So if want set custom behaviour to name property than we modify above class to this
class Person {
var name: String = ""
get() = field.toUpperCase()
set(value) {
field = "Name: $value"
}
}
If the property needs access to its own value in a custom getter or setter (as in this
case), it requires the creation of a backing field. It can be accessed by using field, a
reserved word, and will be automatically created by the compiler when it finds that
it’s being used.
Related
I'm not sure I worded the question correctly.
I have a set of variables, that go like: STO1, STO2, STO3.....STO9; and I need to get the user to input the digit to store and to recall those memory addresses.
So is there a way that the 'STO' be concatenated to the digit (1...9) to get to the var name?
The var names are declared already. I just need to either store a value or retrieve it.
I know that in other languages that is indirect addressing, I think.
Thanks in advance for any input.
Ray.
If variables defined insisde the class (so they are properties) it can be done via Reflection Api.
class Example {
var sto1 = "s1"
var sto2 = "s2"
}
fun main() {
val obj = Example()
val userInput = "1"
val prop = Example::class.memberProperties.find { it.name == "sto$userInput"}
prop as KMutableProperty<*>
//get value example
println(prop.get(obj))
//set value example
prop.setter.call(obj, "new value")
println(prop.get(obj))
}
In order to compile it you should add kotlin-reflect lib to your maven/gradle project.
I want to reference an object within this class I have below:
class HerbData {
object Dill {
const val herbName: String = "This is Dill!"
const val scientificName: String = "Anethum Graveolens"
val dullThumbnail: Int = R.drawable.dill_thumbnail_attr
}
object Peppermint {
val herbName: String = "This is Peppermint!"
}
}
Is there anyway that I can reference the object by using a string in Kotlin? Here is somewhat what I mean:
HerbData."Dill".herbname
I can't find anything on this topic for Kotlin.
Another way you could do this is with an enum class. The advantage over a map is that you have a data structure you can reference directly in code, so you could use HerbData.Dill as well as HerbData["Dill"]. And that will enable you to take advantage of compile-time checking and lint warnings, refactoring, exhaustive pattern matching, code completion etc, because the data is defined in your code
enum class HerbData(
val herbName: String,
val scientificName: String? = null,
val dullThumbnail: Int? = null
) {
Dill("This is Dill!", "Anethum Graveolens", R.drawable.dill_thumbnail_attr),
Peppermint("This is Peppermint!");
companion object {
operator fun get(name: String): HerbData? =
try { valueOf(name) } catch(e: IllegalArgumentException) { null }
}
}
fun main() {
// no guarantee these lookups exist, need to null-check them
HerbData["Peppermint"]?.herbName.run(::println)
// case-sensitive so this fails
HerbData["peppermint"]?.herbName.run(::println)
// this name is defined in the type system though! No checking required
HerbData.Peppermint.herbName.run(::println)
}
>> This is Peppermint!
null
This is Peppermint!
Enum classes have that valueOf(String) method that lets you look up a constant by name, but it throws an exception if nothing matches. I added it as a get operator function on the class, so you can use the typical getter access like a map (e.g. HerbData["Dill"]). As an alternative, you could do something a bit neater:
companion object {
// storing all the enum constants for lookups
private val values = values()
operator fun get(name: String): HerbData? =
values.find() { it.name.equals(name, ignoreCase = true) }
}
You could tweak the efficiency on this (I'm just storing the result of values() since that call creates a new array each time) but it's pretty simple - you're just storing all the enum entries and creating a lookup based on the name. That lets you be a little smarter if you need to, like making the lookup case-insensitive (which may or may not be a good thing, depending on why you're doing this)
The advantage here is that you're generating the lookup automatically - if you ever refactor the name of an enum constant, the string label will always match it (which you can get from the enum constant itself using its name property). Any "Dill" strings in your code will stay as "Dill" of course - that's the limitation of using hardcoded string lookups
The question really is, why do you want to do this? If it's pure data where no items need to be explicitly referenced in code, and it's all looked up at runtime, you should probably use a data class and a map, or something along those lines. If you do need to reference them as objects within the code at compile time (and trying to use HerbData."Dill".herbName implies you do) then an enum is a fairly easy way to let you do both
Declare a Data Class
data class HerbData (
val scientificName: String,
val dullThumbnail: Int
)
Initialize a muteable map and put data in it
val herbData = mutableMapOf<String, HerbData>()
herbData.put("Dill", HerbData("Anethum Graveolens", R.drawable.dill_thumbnail_attr))
herbData.put("Peppermint", HerbData("Mentha piperita", R.drawable.peppermint_thumbnail_attr))
You can now just
herbData["Dill"]?.scientificName
class HerbData {
interface Herb {
val herbName: String
val scientificName: String
}
object Dill : Herb {
override val herbName: String = "This is Dill!"
override val scientificName: String = "Anethum Graveolens"
}
object Peppermint: Herb {
override val herbName: String = "This is Peppermint!"
override val scientificName: String = "Mentha piperita"
}
companion object {
operator fun get(name: String): Herb? {
return HerbData::class
.nestedClasses
.find { it.simpleName == name }
?.objectInstance as? Herb
}
}
}
println(HerbData["Dill"]?.herbName) // Prints: This is Dill!
println(HerbData["Peppermint"]?.scientificName) // Prints: Mentha piperita
println(HerbData["Pepper"]?.herbName) // Prints: null
I started using the data binding library 2 months ago and really like it. But I'm running into an issue. I have for example the following POJO's:
Car{
#Bindable
var name: String
#Bindable
set(value){
field = value
notifyPropertyChanged(BR.name)
}
}
Bike{
#Bindable
var name: String
#Bindable
set(value){
field = value
notifyPropertyChanged(BR.name)
}
}
So basically I have a Car and Bike POJO that share a property name. They both have a name. The problem is that the BR class will only have one of them, it does not generate a prefix for the class. I tested this and the notifyPropertyChanged method for one of them will not work.
This is really annoying. Now I have to put a prefix on both or one of them so they don't match. This solves the problem but then the Bike class becomes:
Bike{
#Bindable
var bikeName: String
#Bindable
set(value){
field = value
notifyPropertyChanged(BR.bikeName)
}
}
This works but I of course do not want to implement my models like this. Anyone that knows how to solve this?
You could use Kotlin Inheritance. You have common fields for both classes.
open class Vehicle : BaseObservable() {
#Bindable
var name: String = ""
#Bindable
set(value) {
field = value
notifyPropertyChanged(BR.name)
}
}
class Bike : Vehicle() {
}
class Car : Vehicle() {
}
I've been using Kotlin to develop some apps in Android and what i want to do currently is to set a field value inside the defining class without calling the setter method.
Here is the code inside my class:
var projectList: List<Project>? = null
set(value) {
saveProjects(value as ArrayList<Project>)
field = value
}
//GO to the database and retrieve list of projects
fun loadProjects(callback: Project.OnProjectsLoadedListener) {
database.projectDao().getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ success ->
callback.onProjectsLoaded(success)
//Here i don't want to save the projects, because i've loaded them from the database
this.projectList = success
},
{ error -> GenericErrorHandler.handleError(error,callback.retrieveContext())}
)
}
Does anybody know a way to do this without calling the set(value) method?
You can only gain access to the field directly from the setter. Inside a setter, the field can be accessed through the invisible field variable.
There are perhaps some other ways around your requirements though. Here are 2 examples. You wouldn't have to follow them exactly, but could instead also combine them to make whatever solution you want.
You could use another shell property to act as the setter for your actual property:
class Example1 {
var fieldProperty: Int = 0
var setterPropertyForField: Int
get() = fieldProperty
set(value) {
fieldProperty = value
}
}
You could use setters as you actually would in Java with a JVM field and a set method. The #JvmField is probably not necessary.
class Example2 {
#JvmField var fieldProperty: Int = 0
fun setField(value: Int) {
fieldProperty = value
}
}
You could probably access the field and change it through reflection, but I don't recommend that approach. That would likely only lead to problems.
I have define the data class as:
data class chatModel(var context:Context?) {
var chatManger:ChatManager?=null
//getter
get() = chatManger
//setter
set(value) {
/* execute setter logic */
chatManger = value
}
}
Now how will i access the get() and set() function.
In java I do like that:
//for getter
new chatModel().getJId()
//for setter
new chatModel().setJId("jid")
edit:
As the #yole suggested. I am using setter and getter as:
//set the data
var chatDetails:chatModel=chatModel(mApplicationContext)
chatDetails.chatManger=chatManager
But end up getting the java.lang.StackOverflowError: at
com.example.itstym.smackchat.Model.chatModel.setChatManger(chatModel.kt:38)
line 38 points to
chatManger = value
this.
#RobCo suggested.
I have change the data class definition as:
data class chatModel(var context: Context?) {
var chatManger:ChatManager
get() = field
set(value) {
field=value
}
}
//set the data.
chatModel(mApplicationContext).chatManger=chatManager
//get the data in different activity
chatModel(applicationContext).chatManger
but getting error property must be initialized. If I assigned it to null then I am getting null not the set value.
You call the setter inside of the setter.. a.k.a. infinite loop:
set(value) {
/* execute setter logic */
chatManger = value
}
Inside a property getter or setter there is an additional variable available: field. This represents the java backing field of that property.
get() = field
set(value) {
field = value
}
With a regular var property, these getters and setters are auto-generated.
So, this is default behaviour and you don't have to override getter / setter if all you do is set the value to a field.
It is important to remember that referring to chatManger ANYWHERE in the code ends up calling getChatManger() or setChatManger(), including inside of the getter or setter itself. This means your code will end up in an infinite loop and cause a StackOverflowError.
Read up on Properties, specifically the section about getters/setters as well as the "backing field".