Polymorphism in Genero 4GL – now in version 3.20

Genero in its latest version has added an interface feature. This feature finally provides the ability to implement polymorphic like features in the language.

What is Polymorphism?

Polymorphism is a feature common in object oriented languages in which different types can have different implementations of the same method. Take this simple example:

We have an interface for a class Shape that implements a method getArea() So any instance of the shape class will be required to implement this method. Then we have two subclasses of Shape class called Circle and Square. When you are calling a method on an instance of a Shape class you will not need to know which type of shape you have. For example:

//initializeing two instances of a shape class.
Shape s1 = new Circle(14);
Shape s2 = new Square(14);
//....
....
..//
//Now at some later point we get the area.  We do not need to know what type of shape we have.
//Depending is the instance is a circle or square shape, the area function 
a1 = s1.getArea()
a2 = s2.getArea()

In the example above, once we initialize the shape, we do not need to know what type of shape it is. When we call the getArea method, the function for that particular shape will automatically be called. That is the basis of polymorphism, two different implementations for the same interface or parent class.

Genero’s version of Polymorphism

Starting with version 3.20, Genero has added the ability to assign an interface to a record type. The interface associates a the different implementations of the same function with the record type. We’ll use the shape example like the one that was described above. First we start with three record types to represent a square, circle and rectangle.

PUBLIC TYPE Circle RECORD
 radius FLOAT
END RECORD

PUBLIC TYPE Square RECORD
 side FLOAT
END RECORD

PUBLIC TYPE Rectangle RECORD
 base FLOAT
,height FLOAT
END RECORD

Next we define an interface type in either the same module or in a new module.

PUBLIC TYPE Shape INTERFACE
 area() returns float
,circumference returns float

In this case, the shape type implements two methods, area and circumference.

Now I need to implement these methods for each of the record types. Usually this will be done in the module that the original records were defined in.

import util
FUNCTION area(c Circle) 
 return util.math.PI*c.radius**2
END FUNCTION

FUNCTION circumference(c Circle) 
 return c.radius*2*util.math.PI
END FUNCTION

FUNCTION area(s Square)
 return s.side**2
END FUNCTION

FUNCTION circumference(s Square)
 return s.side*4
END FUNCTION

FUNCTION area(r Rectangle)
 return r.base*r.height
END FUNCTION

FUNCTION circumference(r Rectangle)
 return r.base*2+r.height*2
END FUNCTION

So now all is left is to connect the interface to the record and implementations. By assigning the shape to the interface variable will also autmatically import the methods that accept the corresponding types.

MAIN
 define
  c Circle
,s Square
,r Rectangle
,shapes DYNAMIC ARRAY OF Shape
,i INT
LET c.diameter = 2.0
LET s.side = 2.0
LET r.base = 2.0
LET r.height = 4.0
shapes[1] = c
shapes[2] = s
shapes[3] = r

FOR i = 1 to shapes.getLength()
#In each case, the appropriate area and circumference implementations will be called.
 DISPLAY "Area: ", i, ":" shapes[i].area()
 DISPLAY "Circumference: ", i, ":", shapes[i].circumference()
END FOR

END MAIN

The difference with other languages is that the relationship between the interface and the type is not defined when the record is defined. The relations ip between the implementations is implicitly created when the record instance is assigned to the interface variable.