Go Interfaces
Links: 103 Golang Index
Interfaces¶
- An interface is a collection of method signatures that an object (struct) must implement.
- Interfaces only have the signatures and not their implementations
- Interfaces define the behaviour of an object.
- Interfaces can achieve polymorphism.
- Can take many dynamic forms at runtime
Interfaces are just tools to make the code more readable, whether we use them or not is upto us.
- To implement an interface in Go we just need to implement all the methods in the interface.
- Go interfaces are implemented implicitly.
- You don't have to explicitly mention if a named type implements an interface.
- In other OOP languages like Java we have to use
implements
keyword to implement an interface.
If a type
implements all the methods in the interface then that type is said to implement that interface. This makes go special.
Its like saying if it walks like a duck, swims like a duck and quacks like a duck then its a duck.
- If a variable is of the interface type then we can call methods that are in the interface type.
Any type that implements the interface is also of the type interface.
A type can implement one or more interfaces.
- The type that implements an interface must implement all the methods otherwise we will get an error.
- A type can have other methods that don't belong to the interface.
Example¶
type circle struct {
radius float64
}
type rectangle struct {
length, breadth float64
}
type shape interface {
area() float64
perimeter() float64
}
func (c circle) area() float64 {
return c.radius * c.radius * 3.14
}
func (c circle) perimeter() float64 {
return c.radius * 2 * 3.14
}
func (r rectangle) area() float64 {
return r.length * r.breadth
}
func (r rectangle) perimeter() float64 {
return r.breadth * r.length * 2
}
// It can take in any type that implements the interface
func print(s shape) {
fmt.Printf("%T\n", s)
fmt.Println(s)
fmt.Println(s.area())
fmt.Println(s.perimeter())
}
func main() {
c1 := circle{radius: 1.}
r1 := rectangle{length: 2., breadth: 4.}
print(c1)
print(r1)
}
// main.circle {1} 3.14 6.28
// main.rectangle {2 4} 8 16
-
Zero value of an interface type is
nil
-
Interfaces have dynamic types that can change during runtime.
Type Assertions and Type Switches¶
- Type assertion provides access to an interface's dynamic concrete value.
- We can only access the methods defined in the interface
- To get access to the underlying dynamic type we use Type Assertion.
- It will extract and return the dynamic value of the interface value
- Type Assertions can fail so it is a good practice to check if it succeeded.
- Type Switch
Embedded Interfaces¶
- In go an interface cannot implement other interfaces or extend them
- We can create a new interface by merging two or more interfaces, this is known as embedding interfaces.
type details interface {
name()
}
type address interface {
location()
}
type employee interface {
details
address
salary()
}
Empty Interface¶
- An empty interface has no methods inside it.
- Any go type will satisfy the empty interface.
- It is a key concept in go
- An empty interface may hold values of any type
type empty interface{}
func main() {
// anything can be stored in an empty interface
var emp empty // we could have also used var emp interface{}
emp = 56
fmt.Printf("%T\n", emp)
emp = "hello"
fmt.Printf("%T\n", emp)
// remember we cannot perform operations on interfaces we would need the concrete/dynamic value for this
// We need to perform type assertion
// fmt.Printf("%v\n", len(emp)) error
fmt.Printf("%v\n", len(emp.(string)))
}
Why are empty interfaces used?
Empty interfaces are used by code that handle values of unknown types.
Empty interfaces are being used to bypass type safety of go. Use empty interfaces only if it is necessary.
- Getting the dynamic value in empty interfaces
Go - Interfaces - Value & pointer receivers¶
Stringer Interface¶
- One of the most ubiquitous interfaces is
stringer
defined by thefmt
package.
- A
Stringer
is a type that can describe itself as a string. Thefmt
package (and many others) look for this interface to print values.
In short String()
method is what gets called when you do a fmt.Println()
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
// Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)
Last updated: 2022-06-30