What's Kotlin Backing Field For? - android

As a Java developer, the concept of a backing field is a bit foreign to me. Given:
class Sample {
var counter = 0 // the initializer value is written directly to the backing field
set(value) {
if (value >= 0) field = value
}
}
What's this backing field good for? Kotlin docs said:
Classes in Kotlin cannot have fields. However, sometimes it is necessary to have a backing field when using custom accessors.
Why? What's the difference with using the properties name itself inside the setter, eg.*
class Sample {
var counter = 0
set(value) {
if (value >= 0) this.counter = value // or just counter = value?
}
}

Because, say if you don't have field keyword, you won't be able to actually set/get the value in the get() or set(value). It enables you to access the backing field in the custom accessors.
This is the equivalent Java code of your sample:
class Sample {
private int counter = 0;
public void setCounter(int value) {
if (value >= 0) setCounter(value);
}
public int getCounter() {
return counter;
}
}
Apparently this is not good, as the setter is just an infinte recursion into itself, never changing anything. Remember in kotlin whenever you write foo.bar = value it will be translated into a setter call instead of a PUTFIELD.
EDIT: Java has fields while Kotlin has properties, which is a rather higher level concept than fields.
There are two types of properties: one with a backing field, one without.
A property with a backing field will store the value in the form of a field. That field makes storing value in memory possible. An example of such property is the first and second properties of Pair. That property will change the in-memory representation of Pair.
A property without a backing field will have to store their value in other ways than directly storing it in memory. It must be computed from other properties, or, the object itself. An example of such property is the indices extension property of List, which is not backed by a field, but a computed result based on size property. So it won't change the in-memory representation of List (which it can't do at all because Java is statically typed).

Initially, I too had a tough time understanding this concept. So let me explain it to you with the help of an example.
Consider this Kotlin class
class DummyClass {
var size = 0;
var isEmpty
get() = size == 0
set(value) {
size = size * 2
}
}
Now when we look at the code, we can see that it has 2 properties i.e - size (with default accessors) and isEmpty(with custom accessors). But it has only 1 field i.e. size. To understand that it has only 1 field, let us see the Java equivalent of this class.
Go to Tools -> Kotlin -> Show Kotlin ByteCode in Android Studio. Click on Decompile.
public final class DummyClass {
private int size;
public final int getSize() {
return this.size;
}
public final void setSize(int var1) {
this.size = var1;
}
public final boolean isEmpty() {
return this.size == 0;
}
public final void setEmpty(boolean value) {
this.size *= 2;
}
}
Clearly we can see that the java class has only getter and setter functions for isEmpty, and there is no field declared for it. Similarly in Kotlin, there is no backing field for property isEmpty, since the property doesn't depend on that field at all. Thus no backing field.
Now let us remove the custom getter and setter of isEmpty property.
class DummyClass {
var size = 0;
var isEmpty = false
}
And the Java equivalent of the above class is
public final class DummyClass {
private int size;
private boolean isEmpty;
public final int getSize() {
return this.size;
}
public final void setSize(int var1) {
this.size = var1;
}
public final boolean isEmpty() {
return this.isEmpty;
}
public final void setEmpty(boolean var1) {
this.isEmpty = var1;
}
}
Here we see both the fields size and isEmpty. isEmpty is a backing field because the getter and setter for isEmpty property depend upon it.

Backing fields are good for running validation or triggering events on state change. Think of the times you've added code to a Java setter/getter. Backing fields would be useful in similar scenarios. You would use backing fields when you needed to control or have visibility over setters/getters.
When assigning the field with the field name itself, you're actually invoking the setter (i.e. set(value)). In the example you have, this.counter = value would recurse into set(value) until we overflow our stack. Using field bypasses the setter (or getter) code.

My understanding is using field identifier as a reference to the property's value in get or set, when you want to change or use the property's value in get or set.
For example:
class A{
var a:Int=1
get(){return field * 2} // Similiar to Java: public int geta(){return this.a * 2}
set(value) {field = value + 1}
}
Then:
var t = A()
println(t.a) // OUTPUT: 2, equal to Java code: println(t.a * 2)
t.a = 2 // The real action is similar to Java code: t.a = t.a +1
println(t.a) // OUTPUT: 6, equal to Java code: println(t.a * 2)

The terminology backing field is filled with mystery. The keyword used is field. The get/set methods, follows immediately next to the member variable that is about to be get or set through this door protective methods mechanism. The field keyword just refers to the member variable that is to be set or get. At present Kotlin, you cannot refer to the member variable directly inside the get or set protective door methods because it will unfortunately result to infinite recursion because it will re-invoke the get or set and thus leds the runtime down into the deep abyss.
In C# though, you can directly reference the member variable inside the getter/setter methods. I am citing this comparison to present the idea that this field keyword is how the present Kotlin is implementing it but I do hope it will be removed in later versions and allow us to directly reference the member variable directly without resulting to infinite recursion.

Related

Comparing two object are indentical in kotlin

I have a two kotlin object which are very identical data without any change, but getting return false. which has to be return true if two object are identical, only if change then it should be return false.
Doing checking objects are:
private var emp1: Employee? = null
var emp2: Employee? = null
fun dataChanged(): Boolean {
return if (emp1 != null && emp2 != null) {
emp1 != emp2
} else {
false
}
}
I checked the data in log, which is not changing anyhing not even space.
Employee defiend as follows,
data class Employee(
//...
): Parcelable {
//...
}
No equals and hashcode.
here using for changing data change on edittext -> TextInputEditText, TextWatcher. Any suggestion, where, i'm doing wrong.
Thanks in advance.
When you define a data class compiler automatically derives the following members from all properties declared in the primary constructor:
equals()/hashCode() pair;
...
Therefore equals method execution depends on parameters of primary constructor.
If you use some other objects in primary constructor make sure they are also data classes or have overriden equals method.
I suggest to put logs before comparison of two objects and check whether they contain equal data.
EDIT:
I have a two kotlin object which are very identical data without any change, but getting return false.
Your function dataChanged() returns false for two identical objects because of condition emp1 != emp2. The name of the function says that it will return true if objects are not identical, false - if they are identical, i.e. data not changed. So the function dataChanged() works as expected.

How can I sort array of numbers higher to lower (reverse/decreasing order)?

How can I sort array by numbers higher to lower.
Array
ArrayList<TestClass> array1 = new ArrayList<>();
Class
public class TestClass{
public boolean type;
public int counter;
public TestClass(boolean type, int counter) {
this.type = type;
this.counter = counter;
}
}
I tried do this
Collections.sort(array1);
But I got error
reason: no instance(s) of type variable(s) T exist so that TestClass conforms to Comparable
Assuming you don't have any accessory methods, you can use
array1.sort(Comparator.comparing(a -> a.counter));
The sorting order you asked for is reverse order, there are couple of ways to achieve this.
You can simple do a reverse of the previous sort like
array1.sort(Comparator.comparing(a -> a.counter));
array1.sort(Collections.reverseOrder());
If you can't user Comparator.comparing, you can do as follows
Collections.sort(array1, (item1, item2) -> Integer.compare(item2.counter, item1.counter));
The above statement can be explained as below.
Collections.sort() is provided from Java collections framework.
First argument specifies which collection needs to be sorted.
Second argument depicts on how each object in the collection should
be evaluated with other object in comparison. So for every pair of objects, in your case integers here, the condition returns true if the second element is greater than the first one. Which will pull the entire list to appear from higher to lower
arrayList.sort(new Comparator<TestClass>() {
#Override
public int compare(TestClass testClass, TestClass t1) {
return Integer.compare(t1.counter, testClass.counter);
}
});

Does overwriting a Float item in the ArrayList in Kotlin cause allocation?

Im recalculating array values inside the ValueAnimator calls so I simply set the values over like
mValues[i] = newValue
Profiler tells me that a Float object is created here each time. There are recommendations for Java to use simple 'float' type here but in Kotlin, this is not an option.
Is that really like this that new Object is created during array update?
Let's check this kotlin code:
fun main(args: Array<String>) {
val list = ArrayList<Int>()
list.add(0)
list[0] = 10
}
Let's decompile it into Java class:
public final class AllocationKt {
public static final void main(#NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
ArrayList list = new ArrayList();
list.add(0);
list.set(0, 10);
}
}
This is decompiled code. Doesn't look like new object was created (at this point).
And now let's check java code - source and decompiled:
public class Allocation {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(0);
arrayList.set(0, 10);
}
}
And decompiled:
public class Allocation {
public Allocation() {
}
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList();
arrayList.add(0);
arrayList.set(0, 10);
}
}
(Almost the same).
Conclusion: Assigning Int into ArrayList<Int> in Kotlin doesn't generate new Integer(i) in Java code. I've checked also for double - still, primitive is used in compiled code.
Update 1:
As I realized later, I've only proved that generated kotlin code is not different than Java code. But I forgot about one very important thing. Generic type arguments must be reference types. So, what happens when I try to put primitive int into list of Integer?
Autoboxing is the answer.
Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes.
So, in bytecode your primitive int will became an Integer, your primitive bool will became Boolean and so on - whenever it's necessary.
Your title says ArrayList, the question itself says "array" twice. Those are different! If you can change to work with primitive arrays (float[] in Java and FloatArray in Kotlin), there will be no allocation.
If you do need a list (e.g. because you want size to change), consider using a primitive collection library: there are many of those for Java, all should be easy to use from Kotlin.

manage null boolean with RxJava2 PublishSubject

I'm implementing the MVP design pattern. My presenter receives the new values from the view. I want to manage the state of a next button by automatically check if everything is valid when values are updated on the view.
In my form I have an optional part which is displayed only if the user select the correct option.
In this optional part I have a binary question. If the part is not displayed I need to set the value of the question to null on the Presenter side.
For example, the user select the option and the optional part is displayed. The user select the answer. Then the user change the option and the optional part is hidden. In that case I need to set the answer to the optional question to null, for the answer to not be already selected if the user display the optional part again.
To do so, I call a method on the Presenter with a null value instead of true/false.
Here is the code:
private final PublishSubject<Boolean> mObsOptionalAnswer = PublishSubject.create();
public MyPresenter(){
// Combine all the values together to enable/disable the next button
Observable.combineLatest(
// ... other fields
// I need this to return false if the optional part is
// displayed but nothing is selected
mObsOptionalAnswer.map(this::isValid),
(...) -> ...
).subscrible(enable ->{
mView.enableBtn(enable);
});
}
public void myFunction(Boolean isSomething){
// ... some code
mObsOptionalAnswer.onNext(isSomething);
}
private boolean isValid(Boolean value){
return value != null;
}
The problem is, since RxJava 2, null values are not allowed in the onNext() method.
So, how am I supposed to manage that?
If you want to be able to send a null value, you can use a wrapper. In this configuration, you send the wrapper, which isn't null even if the value itself is.
public class BooleanWrapper {
public final Boolean value;
public BooleanWrapper(Boolean value) {
this.value = value;
}
}
Your PublishSubject<Boolean> becomes a PublishSubject<BooleanWrapper> and you just have to create the wrapper and de-reference your Boolean when needed :
mObsOptionalAnswer.onNext(new BooleanWrapper(isSomething));
and
mObsOptionalAnswer.map(wrapper -> this.isValid(wrapper.value))
If you need to do that more than once in your code, you can create a generic wrapper (as described by this tutorial) :
public class Optional<M> {
private final M optional;
public Optional(#Nullable M optional) {
this.optional = optional;
}
public boolean isEmpty() {
return this.optional == null;
}
public M get() {
return optional;
}
}
you could use a constante Boolean object
public static final Boolean RESET_VALUE = new Boolean(false);
and you can emit this instead of emitting null. The receiver would have to check against this instance and behaving accordingly. Eg.
.subscrible(enable ->{
if (enable != RESET_VALUE) {
mView.enableBtn(enable);
}
});

how to link my getEmptyForeignCollection() object with my parent object?

I want to persist an object with two foreignCollections.
But when I try to query the object, my foreignId is always null.
I already read this answers but it doesn't really help me: Collections in ORMLite
VOPerception perception = new VOPerception();
perception.setOrientation(daoOrientation.createIfNotExists(
orientationLocalizer.getCurrentOrientation()));
ForeignCollection<VOAccessPoint> fAp =
daoPerception.getEmptyForeignCollection("accessPoints");
fAp.addAll(wifiLocalizer.getCurrentScanResultMap());
perception.setAccessPoints(fAp);
daoPerception.create(perception);
List<VOPerception> list = daoPerception.queryForAll();
here data are correctly stored but VOAccessPoint objects have no link with the parent VOPerception object.
Here are my two classes:
public class VOPerception {
#DatabaseField(generatedId=true)
private int per_id;
#ForeignCollectionField(eager=true)
ForeignCollection<VOAccessPoint> accessPoints;
...
}
public class VOAccessPoint{
#DatabaseField(generatedId=true)
private int ap_id;
#DatabaseField(foreign=true,columnName="apForeignPerception_id")
private VOPerception apForeignPerception;
...
}
Your queryForAll() is returning no objects because none of your VOAccessPoint instances ever set their apForeignPerception field to be perception. Adding the VOAccessPoint objects using the ForeignCollection added them to the DAO but did not automagically assign their apForeignPerception field.
You should do something like:
...
Collection<VOAccessPoint> points = wifiLocalizer.getCurrentScanResultMap();
for (VOAccessPoint point : points) {
point.setApForeignPerception(perception);
}
fAp.addAll(points);
...
I can see how you might think that this would be handled automagically but at the time they are added to the ForeignCollection, the perception is not even assigned. I suspect that there is a missing feature for ORMLite here or at least a better exception.
I would recommend to use assignEmptyForeignCollection(Obj parent, fieldName). This will create a new foreign collection and all objects you will add via add(Obj element) will have the parent value set automatically.

Categories

Resources