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".
Related
First of all I read the link:
Backing Fields
I am not sure I understand from the link, although I saw an example:
Backing Properties
will a backing field be created for a non public property?
This has nothing to do with public/private.
A backing field is always created unless there is a custom getter (and setter for var) and it (neither of them) uses field. A backing field is the only way the value of a property can be stored, regardless of whether it is public. When neither the getter nor setter references field, only then it will not be generated.
In that backing properties example (duplicated below), the _table property has a backing field because it is using the default property implementation. The table property has no backing field because it doesn't use one in its getter.
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // Type parameters are inferred
}
return _table ?: throw AssertionError("Set to null by another thread")
}
I want to create custom request parser, i want to do this by annotating fields and getting value by reflection but I can get annotation only from the class field, the code below doesn't work for data class or constructor in a class, any idea what is wrong?
open class RequestParser {
fun getRequestWithTag(): String {
var requestString = "<RequestData>"
val declaredMemberProperties = this::class.declaredMemberProperties
declaredMemberProperties.filter {
it.findAnnotation<RequestField>() != null
}.forEach { filteredMemberProperties ->
requestString += "<${filteredMemberProperties.name.firstLetterToUpperCase()}>${filteredMemberProperties.getter.call(this)}</${filteredMemberProperties.name.firstLetterToUpperCase()}>"
}
requestString += "</RequestData>"
return requestString
}
}
#Retention(AnnotationRetention.RUNTIME)
#Target(
FIELD,
PROPERTY,
PROPERTY_GETTER,
VALUE_PARAMETER,
PROPERTY_SETTER,
CONSTRUCTOR,
FUNCTION)
public annotation class RequestField
//model example
data class RequestTest(
#RequestField val name: String
) : RequestParser()
//using example
RequestTest("name").getRequestWithTag()
An attribute in a data class constructor is many things, a constructor parameter, a getter, a setter and a field. So you need to set use-site targets to express what you actually mean.
class Example(#field:Ann val foo, // annotate Java field
#get:Ann val bar, // annotate Java getter
#param:Ann val quux) // annotate Java constructor parameter
See also https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets
So in your case I would try the following:
data class RequestTest(
#property:RequestField val name: String
) : RequestParser()
With property I am able to get the annotation from this::class.declaredMemberProperties
If you put field you would be able to get it via this::class.java.declaredFields
I want to create a singleton class, but unfortunately, Android needs Context for almost anything so I need it to create an instance. So I just assumed the user called init(), and then return the instance. As you see below, if the _instance is null, an exception will be thrown, so the get method cannot return null.
But Kotlin says I must initialise instance. The things is, that MyClass cannot be created without a context. So I would like not to specify an initial value. How can I do that?
companion object
{
protected var _instance:MyClass? = null;
fun init(context:Context)
{
_instance = MyClass(context)
}
var instance:MyClass //<---This causes a compile error.
get()
{
if(_instance==null) throw RuntimeException("Call init() first.");
return _instance!!;
}
}
Change the var to val and it should work:
....
val instance: MyClass
....
A variable property (var) not only assumes a getter, but also a setter. Since you provided no setter, a default one was generated set(value) { field = value }. Despite is uselessness in this situation, the default setter uses field, thus requires its initialization.
Use lateinit property
public class MyTest {
lateinit var subject: TestSubject
fun setup() {
subject = TestSubject()
}
fun test() {
subject.method()
}
}
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 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.