Android Example How to Find a Wooly Mammoth Part 1 (Extending the B4A List object functionality using inline Java)

keirS

Well-Known Member
Licensed User
Given the following class and code someone new to B4A might expect a different result to the one which is actually produced by the following code and class definition.

The Pleistocene_Mammal class
B4X:
'Class module
Sub Class_Globals
Dim Species As String
Dim Family As String
End Sub

Public Sub Initialize(mSpecies As String, mFamily As String)
  Species = mSpecies
  Family = mFamily
End Sub
The Activity Code
B4X:
Sub Process_Globals
     Dim Mammals As List
End Sub

Sub Globals
   
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Mammals.Initialize
   
   
    Dim Mammoth As  Pleistocene_Mammal 
    Dim Lion As Pleistocene_Mammal 
    Dim Smilodon As Pleistocene_Mammal 
    Dim Rhino As Pleistocene_Mammal 
   
    Mammoth.Initialize("Wooly Mammoth","Elephant")
    Lion.Initialize("Cave Lion","Cat")
    Smilodon.Initialize("Smilodon","Cat")
    Rhino.Initialize("Wooly Rhino","Rhino")
   
    Mammals.Add(Mammoth)
    Mammals.Add(Lion)
    Mammals.Add(Smilodon)
    Mammals.Add(Rhino)
   
   
    Dim FindMammal As Pleistocene_Mammal 
    FindMammal.Initialize("Wooly Mammoth", "Elephant")
   
     
    If  Mammals.IndexOf(FindMammal) >= 0 Then
        Log("Wooly Mammoth Found! :-)")
    Else
       Log("No Wooly Mammoths :-(")
    End If
End Sub
The code adds 4 Pleistocene_Mammal objects to the Mammals List so our list is
  • Wooly Mammoth
  • Cave Lion
  • Smilodon
  • Wooly Rhino
It then creates another Pleistocene_Mammal object "FindMammal" and searches the Mammals List for it.
The log output will show:
B4X:
"No Wooly Mammoths :-("
This because the IndexOf method is using a method called equals that is a method common to all Java classes.

IndexOf will call the equals method of each object in the list to compare it to the FindMammal object.
By default the equals method does something similar to this in B4A

B4X:
Sub Equals(o As Object) As Boolean)
   Return o = Me

End Sub
Using inline Java we can override the default behavior of the equals method in the Pleistocene_Mammal class to do something more useful.

To start I am going to use an additional Jar file which makes it very easy to build custom equals methods. It's part of the Apache commons Java components and the JAR file can be downloaded here.
So the start of my Activity code now looks like this:

B4X:
#AdditionalJar: commons-lang3-3.3.2
Sub Process_Globals
     Dim Mammals As List
End Sub

Sub Globals
   
End Sub
I now need to override the equals method in the Pleistocene_Mammal class using inline Java..
Pleistocene_Mammal class

B4X:
Sub Class_Globals
Dim Species As String
Dim Family As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(mSpecies As String, mFamily As String)
  Species = mSpecies
  Family = mFamily
End Sub

 #If Java 
         import org.apache.commons.lang3.builder.EqualsBuilder;
       
         @Override
             
         public boolean  equals(Object obj) {
          if (!(obj instanceof pleistocene_mammal))
               return false;
           if (obj == this)
               return true;

         [code
    }
           
 #End If
Taking the Java code in manageable chunks.

B4X:
import org.apache.commons.lang3.builder.EqualsBuilder;
This imports the class EqualsBuilder which I am using to compare one object with the other.

B4X:
@Override
This is a compiler directive telling Java that I am overriding an inherited method.

B4X:
public boolean  equals(Object obj) {
Declares the method equals which returns a boolean type and that it expects an Object as it's parameter type.

B4X:
if (!(obj instanceof pleistocene_mammal))
               return false;
           if (obj == this)
               return true;
Test if the passed Object obj class type is pleistocene_mammal. If it isn't return false. If the class types match then return true if object is the same instance.

B4X:
pleistocene_mammal compare = (pleistocene_mammal) obj;
           EqualsBuilder z = new EqualsBuilder();
The first thing to note here is that when B4A generates Java code it makes all class names lower case. It also makes all the properties lowercase and prefixes them with an underscore. You can check what happens to your properties by looking in your projects objects\src folder.

So the Species property becomes _species and the Family property becomes _family.

The first line casts the passed object obj to the to an instance of the pleistocene_mammal class called compare.

The second line creates an new EqulaBuilder object called z which we are going to use to compare the two objects.

B4X:
              z.append(this._species, compare._species);
               z.append(this._family, compare._family);
This code calls EqualsBuilder append method to add the properties from each object to be compared. This may may not be all the properties in your class as some of them may not matter.

B4X:
if (z.isEquals()) {
                return true;
            } else {
              return false;
            }
}
Finally the isEquals method of the EqualsBuilder object is called to compare the properties. If they are equal true is returned otherwise false is returned.

I have attached the sample code project and when you run it you should find the the log shows:

B4X:
"Wooly Mammoth Found! :-)"
In Part 2 I will explain how to extend this to cover more complex cases where you have a hierarchy of contained objects.
 

Attachments

thedesolatesoul

Expert
Licensed User
Interesting. I didnt really see where this was going till the end.
I would question though as an object when is Mammoth = FindMammal? Technically you created two different instances of the class, so the usual B4A/Java equals is correct to tell you they are different (and that is the usual use case). For e.g. there are times when you need to know that the Mammoth created was the same one you instantiated in the first place.
Secondly, the equals method you overrided and created, is testing for the class parameters to be equivalent. I dont see the benefit of doing this (even if the members are private) in java, when you could create the same equivalency code in B4A. There is no benefit apart from being able to use List.IndexOf (and similar).
Personally I feel this creates more confusion (atleast in my approach of dealing with equivalency of instances).
I am interested though, to see how else this can be extended.
 

keirS

Well-Known Member
Licensed User
Interesting. I didnt really see where this was going till the end.
I would question though as an object when is Mammoth = FindMammal? Technically you created two different instances of the class, so the usual B4A/Java equals is correct to tell you they are different (and that is the usual use case). For e.g. there are times when you need to know that the Mammoth created was the same one you instantiated in the first place.
Secondly, the equals method you overrided and created, is testing for the class parameters to be equivalent. I dont see the benefit of doing this (even if the members are private) in java, when you could create the same equivalency code in B4A. There is no benefit apart from being able to use List.IndexOf (and similar).
Personally I feel this creates more confusion (atleast in my approach of dealing with equivalency of instances).
I am interested though, to see how else this can be extended.
A real world use case would be where I am using a list to store the lines for a sales order. I don't want duplicate lines with the same part number/SKU on them. I want to see if that part is already in the list and amend the quantity. Arguably I could use a Mao and use the part number as the key. But it maybe my part has the same SKU but differing attributes such as color. I am not suggesting that every object's equals method should be overridden. Just that in some cases it is useful to do so.
 

thedesolatesoul

Expert
Licensed User
A real world use case would be where I am using a list to store the lines for a sales order. I don't want duplicate lines with the same part number/SKU on them. I want to see if that part is already in the list and amend the quantity. Arguably I could use a Mao and use the part number as the key. But it maybe my part has the same SKU but differing attributes such as color. I am not suggesting that every object's equals method should be overridden. Just that in some cases it is useful to do so.
I think the case you mentioned has conflicts. For e.g. if an attribute is different, it is either the same object or not (depending on whether you consider it in equivalency). In that case you need a different ID or not. A map would be a better data structure for it.
 

keirS

Well-Known Member
Licensed User
I think the case you mentioned has conflicts. For e.g. if an attribute is different, it is either the same object or not (depending on whether you consider it in equivalency). In that case you need a different ID or not. A map would be a better data structure for it.
Wait for the second part where I will be showing how to handle objects that contain other objects. It should become clearer then. To give you and idea it involves overriding the hashCode method in the contained objects and including the hashes in the equivalence test for the container object. I should have really included the hashCode method in this tutorial because it's considered really bad practice to override one of the methods and not the other. But I wanted to do it in small chunks.
 

ivan.tellez

Active Member
Licensed User
I think the case you mentioned has conflicts. For e.g. if an attribute is different, it is either the same object or not (depending on whether you consider it in equivalency). In that case you need a different ID or not. A map would be a better data structure for it.
I think that the purpose of this tutorial is to sow how to "Extend the B4A List object functionality using inline Java" The author overriding the equals was just an example to fit his needs. He is not telling that this is allways the correct way to compare objects.
 

thedesolatesoul

Expert
Licensed User
I think that the purpose of this tutorial is to sow how to "Extend the B4A List object functionality using inline Java" The author overriding the equals was just an example to fit his needs. He is not telling that this is allways the correct way to compare objects.
I know that. But I still am wondering what a good use case for this is.
I do find the need to extend classes at times, but I would like to see how this fits.
Still waiting for the second part though.
 
Top