Android Tutorial Classes tutorial

Discussion in 'Tutorials & Examples' started by Erel, Jun 12, 2012.

  1. Erel

    Erel Administrator Staff Member Licensed User

    Basic4android v2.00 adds support for classes modules.

    Classes definition from Wikipedia:
    Example:
    Code:
    'Class Person module
    Sub Class_Globals
       
    Private FirstName, LastName As String
       
    Private BirthDate As Long
    End Sub

    Sub Initialize (aFirstName As String, aLastName As String, aBirthDate As Long)
       FirstName = aFirstName
       LastName = aLastName
       BirthDate = aBirthDate
    End Sub

    Public Sub GetName As String
       
    Return FirstName & " " & LastName
    End Sub

    Public Sub GetCurrentAge As Int
       
    Return GetAgeAt(DateTime.Now)
    End Sub

    Public Sub GetAgeAt(Date As Long) As Int
       
    Dim diff As Long
       diff = Date - BirthDate
       
    Return Floor(diff / DateTime.TicksPerDay / 365)
    End Sub

    'Main module
    ...
    Dim p As Person
    p.Initialize(
    "John""Doe"DateTime.DateParse("05/12/1970"))
    Log(p.GetCurrentAge)
    I will start by explaining the differences between classes, code modules and types.

    Similar to types, classes are templates. From this template you can instantiate any number of objects.
    The type fields are similar to the classes global variables. However unlike types which only define the data structure, classes also define the behavior. The behavior is defined in the classes subs.

    Unlike classes which are a template for objects, code modules are collections of subs. Another important difference between code modules and classes is that code modules always run in the context of the calling sub (the activity or service that called the sub). The code module doesn't hold a reference to any context. For that reason it is impossible to handle events or use CallSub with code modules.
    Classes store a reference to the context of the activity or service module that called the Initialize sub. This means that classes objects share the same life cycle as the service or activity that initialized them.

    Code modules are somewhat similar to singleton or static classes.

    Adding a class module
    Adding a new or existing class module is done by choosing Project -> Add New Module -> Class module or Add Existing module.
    Like other modules, classes are saved as files with bas extension.

    Classes structure
    Classes must have the following two subs:

    Class_Globals - This sub is similar to the activity Globals sub. These variables will be the class global variables (sometimes referred to instance variables or instance members).

    Initialize - A class object should be initialized before you can call any other sub. Initializing an object is done by calling the Initialize sub. When you call Initialize you set the object's context (the parent activity or service).
    Note that you can modify this sub signature and add arguments as needed.

    In the above code we created a class named Person and later instantiate an object of this type:
    Code:
    Dim p As Person
    p.Initialize(
    "John""Doe"DateTime.DateParse("05/12/1970"))
    Log(p.GetCurrentAge)
    Calling initialize is not required if the object itself was already initialized:
    Code:
    Dim p2 As Person
    p2 = p 
    'both variables now point to the same Person object.
    Log(p2.GetCurrentAge)
    Polymorphism
    Polymorphism allows you to treat different types of objects that adhere to the same interface in the same way.
    Basic4android polymorphism is similar to the Duck typing concept.

    As an example we will create two classes named: Square and Circle.
    Each class has a sub named Draw that draws the object to a canvas:
    Code:
    'Class Square module
    Sub Class_Globals
       
    Private mx, my, mLength As Int
    End Sub

    'Initializes the object. You can add parameters to this method if needed.
    Sub Initialize (x As Int, y As Int, length As Int)
       mx = x
       my = y
       mLength = length
    End Sub

    Sub Draw(c As Canvas)
       
    Dim r As Rect
       r.Initialize(mx, my, mx + mLength, my + mLength)
       c.DrawRect(r, 
    Colors.White, False1dip)
    End Sub
    Code:
    'Class Circle module
    Sub Class_Globals
       
    Private mx, my, mRadius As Int
    End Sub

    'Initializes the object. You can add parameters to this method if needed.
    Sub Initialize (x As Int, y As Int, radius As Int)
       mx = x
       my = y
       mRadius = radius
    End Sub

    Sub Draw(cvs As Canvas)
       cvs.DrawCircle(mx, my, mRadius, 
    Colors.Yellow, False1dip)
    End Sub
    In the main module we create a list with Squares and Circles. We then go over the list and draw all the objects:
    Code:
    Sub Process_Globals
       
    Dim shapes As List
    End Sub

    Sub Globals
       
    Dim cvs As Canvas  
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       cvs.Initialize(
    Activity)
       
    Dim sq1, sq2 As Square
       
    Dim circle1 As Circle
       sq1.Initialize(
    100dip100dip50dip)
       sq2.Initialize(
    2dip2dip100dip)
       circle1.Initialize(
    50%x50%y100dip)
       shapes.Initialize
       shapes.Add(sq1)
       shapes.Add(sq2)
       shapes.Add(circle1)
       DrawAllShapes
    End Sub

    Sub DrawAllShapes
       
    For i = 0 To shapes.Size - 1
          
    CallSub2(shapes.Get(i), "Draw", cvs) 'Call Draw of each object
       Next
       
    Activity.Invalidate
    End Sub
    (the example code is attached)

    As you can see, we do not know the specific type of each object in the list. We just assume that it has a Draw method that expects a single Canvas argument. Later we can easily add more types of shapes.
    You can use the SubExists keyword to check whether an object includes a specific sub.

    You can also use the Is keyword to check if an object is of a specific type.

    Self reference
    The Me keyword returns a reference to the current object. 'Me' keyword can only be used inside a class module.
    Consider the above example. We could have passed the shapes list to the Initialize sub and then add each object to the list from the Initialize sub:
    Code:
    Sub Initialize (Shapes As List, x As Int, y As Int, radius As Int)
       mx = x
       my = y
       mRadius = radius
       Shapes.Add(Me) 
    'Me is used to add this object to the list
    End Sub
    Activity object
    This point is related to the activities special life cycle. Make sure to first read the activities and processes life-cycle tutorial.

    Android UI elements hold a reference to the parent activity. As the OS is allowed to kill background activities in order to free memory, UI elements cannot be declared as process global variables (these variables live as long as the process lives). Such elements are named Activity objects. The same is true for custom classes. If one or more of the class global variables is of a UI type (or any activity object type) then the class will be treated as an "activity object". The meaning is that instances of this class cannot be declared as process global variables.

    Properties
    Starting from B4A v2.70, classes support properties. Properties syntax can be considered a syntactic sugar.
    Properties combine two methods into a single "field" like member.
    For example the two following methods:
    Code:
    'Gets or sets the text
    Sub getText As String
       
    Return btn.Text
    End Sub

    Sub setText(t As String)
       btn.Text = t
    End Sub
    Are merged automatically into a single property:

    [​IMG]

    The property can be treated like any other field:
    Code:
    Dim c1 As SomeClass
    c1.Text = 
    "abc"
    Log(c1.Text)
    The rules for properties:
    - Only relevant for classes.
    - One or two subs with the format get<prop> / set<prop>. Note that get / set must be lower case.
    - A property can be read-only (only get), write-only (only set) or both.
    - The two subs types (parameter in the set sub and return type in the get sub) must be the same.
    - Within the class you should call the methods directly. The property will not appear.
    - The property cannot have the same name as a global variable.

    Related links:
    Built-in documentation
    Variables & Objects
    Variables & Subs visibility
     

    Attached Files:

    • Draw.zip
      File size:
      7.1 KB
      Views:
      1,235
    Last edited: Nov 24, 2013
  2. klaus

    klaus Expert Licensed User

    Looks very interesting and powerful !

    Best regards.
     
    Last edited: Jun 12, 2012
  3. splatt

    splatt Active Member Licensed User

    Time to dust off some of my old books and start delving into OO again.

    Awesome work Erel!
     
    MrChemist likes this.
  4. netchicken

    netchicken Active Member Licensed User

    This really is impressive. I am really looking forward to getting into this.
     
  5. wl

    wl Well-Known Member Licensed User

    Great features !

    One question though: is there a possibility to add a static method to a class ?
     
  6. Erel

    Erel Administrator Staff Member Licensed User

    There are no static variables or methods in classes. You can use a code module for that.
     
  7. wl

    wl Well-Known Member Licensed User

    ok thanks Erel, so a static member "belonging" to a class can not be written in the same file ?

    Also (related): is the name of the class automatically the same as the name of the file in which its code resides ? If so you can also have just a single class in a single file ?

    Thanks !
     
  8. NeoTechni

    NeoTechni Well-Known Member Licensed User

    Is there also a deconstructor/deinitialize event?
    That way we can clear our stuff properly if needed.
     
  9. Roger Garstang

    Roger Garstang Well-Known Member Licensed User

    Should Class_Globals be a visible method? Seems like that would only be called once and sets up the Globals. I noticed the Activity Globals is visible too, but can't really think of a reason for calling either of them on their own. It just seems like that would mess something up...especially after initializing the object.

    When is that sub actually called by the system to dim the globals in classes and activities- When the object is created or when you call initialize? I don't have to call initialize on an Activity, but it is there so it seems something calls it and passes it "Activity" as the Event Prefix by default. I never really thought about when the Activity Global sub is called since either way it was already called beforehand.

    I think I like the term Instance Variables that you use in the tutorial better too. When I hear Class_Globals I think more along the lines of Static variables Global to all classes. Perhaps calling it Sub Instance_Variables?

    The term Globals is sort of overused everywhere. It is confusing that every module has a Process Globals and that you can dim the same variable name in every module yet they are really all different variables and you have to use the module name to access them. Process Globals should be like another module.

    And, Activity Globals really aren't Global either since they are only "Global" to the activity they are more like Class Instance Variables. Activity Globals probably could have been called Activity_Variables. The subs can do more than DIM variables too, they can act on them as well as other variables, so maybe they should be called Process_Start and the Activity Globals either just Activity or Initialize just like the classes.

    It gets more confusing the more I think about it though since really the two Class functions are more like Activity Globals and Activity_Create, but when you call Activity.Initialize it isn't the same as calling Activity_Create. And if the Globals functions are always called when the Activity is Created or the Class is initialized why two subs? I'd say at least 90% of the time the Globals subs are just dimming variables which could be done at the top of Activity_Create/Class Initialize. Once we get something like Static variables I can see a reason for separation, but it seems redundant and confusing for beginners to split it so much and have so many steps.
     
  10. Erel

    Erel Administrator Staff Member Licensed User

    That is correct.
    Yes and yes (creating multiple classes in the same file may be added in the future).

    No. The Garbage collector will take care for releasing the memory allocated to the object. If you open a file and need to close it then you should add a Close method and the user will need to call it.

    About the globals. There are two types of variables in each module. Local variables and global variables. Global variables are variables declared in a globals sub. Local variables are variables declared inside any other sub.

    The global subs should only be used to declare variables. Declaring a variable in Activity_Create or Initialize sub will declare a local variable not a global variable.
     
  11. Roger Garstang

    Roger Garstang Well-Known Member Licensed User

    That is the other thing I thought of after posting too was how the global subs sort of breaks the rules and the scope of variables within them extend beyond the sub. With the new Public and Private options defining scope couldn't we use those and/or another specifier like Global and instead of declaring the variables in a sub we declare them outside subs much like a Class (Not a B4A Class, but Classes in other languages) does anyway?

    This would eliminate the need for all of the extra subs. Private and Public declares apply to the Activity/Class and if you define it as Global it is Process wide and not tied to a module (I still think it would be good to have process variables shown another way too so not tied to a module, and some type of Process_Start sub/module to do all Process level things). I'm not saying the current way is bad or anything...it works, but can get confusing with everything split the way it is. The biggest issue would be porting old code to a new way and would take some tweaking and moving of things to keep the same variable names. There is a version number stored in the project though which helps.
     
    Last edited: Jun 13, 2012
  12. Erel

    Erel Administrator Staff Member Licensed User

    It was a design decision taken in v1.00. I think that it is reasonable and it definitely not worth changing now.
     
  13. Jost aus Soest

    Jost aus Soest Active Member Licensed User

    Great impovements! :icon_clap:

    But one think is somewhat inelegant:

    Instead of writing this:
    Code:
    CallSub2(shapes.Get(i), "Draw", cvs) 'Call Draw of each object
    I had expected to write something like this:
    Code:
    shapes.Get(i).Draw(cvs) 'Call Draw of each object
    1. Is there any hope that we can get this in the (near) future?
    2. Is real functional polymorphism still a topic?

    Jost, back from holidays!
     
  14. Erel

    Erel Administrator Staff Member Licensed User

    This syntax can only work with known types. In this case the type is unknown. The compiler must treat it differently.

    What do you mean with real functional polymorphism?
     
  15. wl

    wl Well-Known Member Licensed User

    I think what is meant is polymorphism by means of inheritance (or interfaces), so that for question 1 the Draw method could be called in all objects (instead of the callsub)
     
  16. CharlesIPTI

    CharlesIPTI Active Member Licensed User

    Version availability

    So is 2.0 available yet . If so where can I get one
     
  17. Erel

    Erel Administrator Staff Member Licensed User

  18. Jost aus Soest

    Jost aus Soest Active Member Licensed User

    Exactly!
     
  19. Erel

    Erel Administrator Staff Member Licensed User

    Each approach has advantages and disadvantages. CallSub is a more "dynamic" solution.

    Note that the (very useful) "Find all references" tool will show you CallSub usages as well.
     
  20. Jost aus Soest

    Jost aus Soest Active Member Licensed User

    The main advantages of real polymorphism are:
    1. It's type safe.
    2. IDE can show such methods when pressing ".".
    3. The code is easier to read.
     
Loading...