Learning GoLang - Day 3 : Interfaces are simple, but not easy to master!
From ChatGPT 3.5
Introduction to Interfaces: Interfaces in Go provide a way to define behavior without specifying the underlying type. They are contracts that define a set of methods. This allows for polymorphism and enables writing flexible and reusable code.
We are allowed to create customized data types or structures in Golang. However, it is still not an Object-Oriented Language like C++ or Java, which provides classes to facilitate some scenarios. We are unable to create functions inside of any type. Even though OOP is not mandatory to evaluate the needed functionalities, to offer developers an easier, more elegant, and convenient way of programming in use cases like producer-customer, Golang offers OO concepts: methods and interfaces.
Let's begin by experiencing them through the following snippets.
// customized type
type Vertex struct {
X, Y float64
}
// value receiver argument
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X * v.X + v.Y * v.Y)
}
// pointer receiver argument
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
The above example is taken from "A Tour of Go", which is typical and clear. The so-called "methods" are actually "functions" with an additional argument before the name of the function. On the first day of learning, we noticed that Golang only passes arguments by value, so it makes sense to use a pointer receiver if you want to modify its value. It also brings other benefits like avoiding copies. To use these methods, you need to first declare a variable v := Vertex{1,3}
(or v := &Vertex{1,3}
), then call v.Scale(10)
and v.Abs()
.
Of course, you could implement regular functions to achieve the same functionalities as well, losing some convenience and comprehensibility.
To enhance the benefits and scalability of methods, an interface type is defined, as a set of method signatures. A value of interface type can hold any value only if its type implements the interface by implementing the corresponding methods with receivers. A specific example is as follows(from "A Tour of Go"):
type Abser interface {
Abs() float64
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
// In the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
// a = v
fmt.Println(a.Abs())
}
As a C++ developer, its logic is a bit counterintuitive. The =
operator never shows such functionality as binding the value of any data type to the value of an interface type.
So far, we only glanced at the basic usage of interfaces in Go, tomorrow I will continue the chapter of interfaces, to talk about the key differences, tradeoffs or even pitfalls comparing interfaces in OOP. See you there!