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

Discussion in 'Tutorials & Examples' started by keirS, Feb 27, 2015.

  1. keirS

    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
    Code:
    '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
    Code:
    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:
    Code:
    "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

    Code:
    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:

    Code:
    #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

    Code:
    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.

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

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

    Code:
    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.

    Code:
    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.

    Code:
    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.

    Code:
    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.

    Code:
    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:

    Code:
    "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.
     

    Attached Files:

  2. stevel05

    stevel05 Expert Licensed User

    Very useful, thanks for sharing:)
     
  3. thedesolatesoul

    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.
     
    NJDude likes this.
  4. keirS

    keirS Well-Known Member 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.
     
  5. thedesolatesoul

    thedesolatesoul Expert 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.
     
    NJDude likes this.
  6. keirS

    keirS Well-Known Member Licensed User

    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.
     
  7. ivan.tellez

    ivan.tellez Active Member 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.
     
  8. thedesolatesoul

    thedesolatesoul Expert Licensed User

    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.
     
    ivan.tellez likes this.
  9. keirS

    keirS Well-Known Member Licensed User

    Been at bit busy lately. But will hopefully get time sort out the second part next week.
     
    koaunglay and thedesolatesoul like this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice