Rick

Rick
Rick

Tuesday, January 15, 2013

Java Guide to Go Programming article 4: Polymorphism II

In the last article (article 3), we covered polymorphism in Go. Go does not have type inheritance like C++, Java, Python, Ruby, C#, Dart, ... well you get the idea. It does have inheritance via structs but you inherit data and behavior (functions), not types.


Go inheritance is via structs and anonymous fields unlike well, umm, err everybody else (see last article to see an example of this). Polymorphism is mostly achievable via interfaces. In the Java sense, only Go interfaces specify types not classes, umm, err, structs. From an interface you can cast to another interface if the underlying object implements that interface.


The last set of Go examples showed something that really looked like Java method overriding. It looked like it. Smelled like it and even behaved like it, but none of it was possible without the Go interface.


You may have heard of this principle if you are long time Java or C# developer: program to interfaces. Well in Go, you don't have much choice. Interfaces not classes or structs are the true road to polymorphic behavior.


The functionality from Employee.Name() was inherited by SalesEmployee and HourlyEmployee. Also HourlyEmployee and SalesEmployee appeared to override the behavior of Employee.Calc with HourlyEmployee.Calc and SalesEmployee.Calc. However, you can't store a list of Employee instances in a Go array or a Go slice (a Go slice is like a java.util.List while a Go array is like well a Java array). An Employee instance is always an Employee instance in Go. A SalesEmployee is always a SalesEmployee. It is not like Java where a SalesEmployee is an Employee. To get the polymorphic behavior in our example, we had store the list of employees in a Payable interface array. The typing, if you will, is from the interface not the struct. The interface is the contract for behavior. A SalesEmployee, Employee and HourlyEmployee are all Payable instances so to speak.


Let's review briefly.

Revisit Polymorphism example in GO

/examples/src/example/usePolymorphism.go
package polymorphism


type Payable interface {
 Name() string
 CalculatePay() float64
}

/* Notice that Employee implements the Payable interface but we dont have to say it does. There is no Employee implements Payable like in Java and C#. */

type Employee struct {
 firstName string
 lastName  string
 age       int
 current   bool
}

func (this *Employee) CalculatePay() float64 {
 return 0.0
}

func (this *Employee) Name() string {
 return this.firstName + " " + this.lastName
}

func NewEmployee(firstName string, lastName string, age int, current bool) *Employee {
 return &Employee{firstName, lastName, age, current}
}

...

type HourlyEmployee struct {
 Employee //HourlyEmployee extends Employee by using this anonymous field
 hourlyRate float64
}

/* 
HourlyEmployee.CalculatePay....
This CalculatePay effectively overrides the one defined in Employee. 
HourlyEmployee inherits the data from Employee and the method Name().
*/
func (this *HourlyEmployee) CalculatePay() float64 {
 return this.hourlyRate * 1000.0 //lookup hours worked in DB, but for this just hard code
}

func NewHourlyEmployee(firstName string, lastName string, age int, current bool, hourlyRate float64) *HourlyEmployee {
 return &HourlyEmployee{Employee{firstName, lastName, age, current}, hourlyRate}
}

...

type SalesEmployee struct {
 Employee //SalesEmployee extends Employee by using this anonymous field
 commisionRate float64
}

/* 
SalesEmployee.CalculatePay....
This SalesEmployee.CalculatePay effectively overrides the one defined in Employee. 
SalesEmployee inherits the data from Employee and the method Name() from Employee.
*/
func (this *SalesEmployee) CalculatePay() float64 {
 return this.commisionRate * 100000000000
}

func NewSalesEmployee(firstName string, lastName string, age int, current bool, commisionRate float64) *SalesEmployee {
 return &SalesEmployee{Employee{firstName, lastName, age, current}, commisionRate}

}

You will read that Go programming does not allow method overriding and while technically true, if you can define an interface like Payable, then you can treat a motley group of as a single type and you get what looks like method overriding and inheritance in Java.

Revisit Polymorphism example in GO

package main

import (
 "example/polymorphism"
 "fmt"
)

func main() {

 /* employee1 is a Employee, employee2 is a SalesEmployee, and employee3 is a HourEmployee, but
    employee2 is not an Employee, and employee3 is not an Employee. Type inheritance does not exist. */
 employee1 := polymorphism.NewEmployee("Rick", "Hightower", 32, true)
 employee2 := polymorphism.NewSalesEmployee("Whitney", "Hightower", 17, true, 0.05)
 employee3 := polymorphism.NewHourlyEmployee("Bob", "Hightower", 27, true, 25.0)

 
 // You can't store them in an array of Employees like you could in Java.
 //employees := []*polymorphism.Employee{employee1, employee2, employee3}
 
 //You can store them in an array of Payable interfaces
 employees := []polymorphism.Payable{employee1, employee2, employee3}
 

 for _, employee := range employees {
  fmt.Printf("%s %2.2f \n", employee.Name(), employee.CalculatePay())
 }
 
 /*  You can't cast one employee type to another like this. */
 //emp := employee2.(polymorphism.Employee) //Error Go does not have type inheritance!
 var obj interface{} = employee1  //interface {} is sort of like Java's Object as Go is interface centric
 emp := obj.(polymorphism.Payable) //This is like a cast to Payable, emp is Payable not employee
 fmt.Println(emp)
 
 //var emp2 *polymorphism.Employee = employee2 //Error Go does not have type inheritance, 
             // you a SalesEmployee can never be a Employee
 var emp2 *polymorphism.SalesEmployee = employee2 //This is allowed because employee2 is a SalesEmployee
 fmt.Println(emp2)
 
 /* All polymorhism, method overiding, is done through interfaces not struct instances. */
}

In the Java realm, there are also interfaces. In Java both interfaces and classes can define the type of an instance. In Go, it is really just the interface that allows instances to be grouped together.

Revisit Java Polymorphism example this time with interfaces


package com.example.polymorphism2;

public interface Payable {
 String name();
 double calculatePay();
}

...
package com.example.polymorphism2;

public class Employee implements Payable {
 protected String firstName;
 protected String lastName;
 protected int age;
 protected boolean current;
 
 public Employee(String firstName, String lastName, int age, boolean current) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
  this.current = current;
 }
 
 public double calculatePay()  {
  return 0.0d;
 }
 
 public String name() {
  return this.firstName + " " + this.lastName;
 }

...
package com.example.polymorphism2;

public class HourlyEmployee extends Employee {

 private double hourlyRate;

 public HourlyEmployee(String firstName, String lastName, int age,
   boolean current, double commisionRate) {
  super(firstName, lastName, age, current);
  this.hourlyRate = commisionRate;
 }
 
 public double calculatePay()  {
  return this.hourlyRate * 1000.0; //lookup hours worked in DB, but for this just hard code
 }


...
package com.example.polymorphism2;

public class SalesEmployee extends Employee {

 private double commisionRate;

 public SalesEmployee(String firstName, String lastName, int age,
   boolean current, double commisionRate) {
  super(firstName, lastName, age, current);
  this.commisionRate = commisionRate;
 }
 
 public double calculatePay()  {
  return this.commisionRate * 100000000000l; 
 }
...
package com.example;

import com.example.polymorphism2.Employee;
import com.example.polymorphism2.HourlyEmployee;
import com.example.polymorphism2.SalesEmployee;
import com.example.polymorphism2.Payable;

public class UsePolymorphism2 {

 public static void main(String[] args) {
  Employee employee1 = new Employee("Rick", "Hightower", 32, true);
  SalesEmployee employee2 = new SalesEmployee("Whitney", "Hightower", 17,
    true, 0.05);
  HourlyEmployee employee3 = new HourlyEmployee("Bob", "Hightower", 27,
    true, 25.0);

  Employee[] employees = new Employee[] { employee1, employee2, employee3 };

  for (Employee employee : employees) {
   System.out.printf("%s %2.2f \n", employee.name(),
     employee.calculatePay());
  }

  /*  You can't cast one employee type to another like this in Go, but no problem for Java */
  Employee emp = employee2; //No Error for Java as it does have type inheritance
  Payable payable = employee2; //Works with interfaces too
  System.out.println(emp);
  System.out.println(payable);

  
  SalesEmployee saleEmployee = (SalesEmployee) emp; //You can downcast and up cast as type inheritance is its things
  System.out.println(saleEmployee);
 }
}

Can you achieve more parity with Java method overriding in Go. Not really. You can create something that is more similar (or less similar in concept) that allows you to switch out methods at runtime, but Go is Go and Java is Java. Embrace Go's emphasis on interfaces and its style and brand of polymorphism and you will learn to love it. It is the loose coupling and late bound interfaces that allow great management of dependencies and that speed up Go compilation. And if you feel design by interface was good in Java then you will love Go because it forces it.
Let's take another Go at doing method overriding in Go. This time I will start with the main method.

Revisit Go Polymorphism example this time with no interfaces


package main

import (
 "example/override"
 "example/polymorphism" 
 "fmt"
)

func main() {
 employee1 := override.NewEmployee("Rick", "Hightower", 32, true)
 employee2 := override.NewSalesEmployee("Whitney", "Hightower", 17, true, 0.05)
 employee3 := override.NewHourlyEmployee("Bob", "Hightower", 27, true, 25.0)

 employees := []*override.Employee{employee1, employee2, employee3}

 for _, employee := range employees {
  fmt.Printf("** %s %2.2f \n", employee.Name(), employee.CalculatePay())
 }
 
 /*  They are all employees so this is no longer a problem */
 emp := employee2 
 fmt.Println(emp)

 var obj interface{} = employee1  //interface {} is sort of like Java's Object as Go is interface centric
 emp1 := obj.(polymorphism.Payable) //This is like a cast to Payable, emp is Payable not employee
 fmt.Println(emp1)

}


I know what you are thinking. I just told you that you can't group HourlyEmployee, SalesEmployee and regular Employees into one array, but then I write an example were ya do it. Tricky Ricky. I assure you when this program executes the same behavior as before and it changes polymorphically too. Before you look at the answer, ponder how I did this given the above statements of types not being inheritable.
Times up. I cheated. Go programming has first class functions. Functions are objects. They can be assigned. The instances employee1, employee2, and employee3 are all Employees now. There is no HourlyEmployee struct or SalesEmployee struct. When I construct the Employees, I populate the Employee struct with the right functions and this gives me the desired polymorphic behavior but all with one type, i.e., Employee. I utilize closures and lambda functions to capture hourlyRate and commisionRate.

Revisit Go Polymorphism example this time with no interfaces

package override

type empType int

const (
 hourly empType = iota
 sales empType = iota
 regular empType = iota 
)

type Employee struct {
 firstName        string
 lastName         string
 age              int
 current          bool
 calculatePayFunc func(this *Employee) float64
 nameFunc         func(this *Employee) string
 etype empType 
}

func (this *Employee) CalculatePay() float64 {
 calcFunc := this.calculatePayFunc
 return calcFunc(this)
}

func (this *Employee) Name() string {
 nameFunc := this.nameFunc
 return nameFunc(this)
}

func newEmployee(firstName string, lastName string, age int, current bool,
 calcFunc func(this *Employee) float64, nameFunc func(this *Employee) string, etype empType) *Employee {
 if nameFunc == nil {
  nameFunc = func(this *Employee) string { return lastName + " " + firstName }
 }
 if calcFunc == nil {
  calcFunc = func(this *Employee) float64 { return 1000 }
 }
 
 return &Employee{firstName, lastName, age, current, calcFunc, nameFunc, etype}
}

func NewEmployee(firstName string, lastName string, age int, current bool) *Employee {
 return newEmployee(firstName, lastName, age, current, nil, nil, regular)
}

func NewHourlyEmployee(firstName string, lastName string, age int, current bool, hourlyRate float64) *Employee {
 calcFunc:= func (this *Employee)  float64 {return hourlyRate * 1000}
 return newEmployee(firstName, lastName, age, current, calcFunc, nil, hourly)
}


func NewSalesEmployee(firstName string, lastName string, age int, current bool, commisionRate float64) *Employee {
 calcFunc:= func (this *Employee)  float64 {return commisionRate * 100000000000}
 return newEmployee(firstName, lastName, age, current, calcFunc, nil, sales)
}

...
Now let me show you how you could do the above with Java closures and lambda support, but first I need to invent a time machine because that doesn't come out until Java 8.
I don't know what is going to be in article 5 yet, but I am open to suggestions.

No comments:

Post a Comment

Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training