Introduction
- Interfaces are a collection of method signatures.
type shape interface {
area() int
perimeter() int
}
In the above example, shape
is anything that can be used to calculate its area and parameter. If you check the example below, you can see that rectangle
implements both the area()
and perimeter()
methods. So, it means rectangle
implements the shape
interface.
type rectangle struct {
length int
width int
}
func (r rectangle) area() int {
return r.length * r.width
}
func (r rectangle) perimeter() int {
return (r.length + r.width) * 2
}
- Multiple types can implement the same interface. If there is another struct named
circle
witharea()
andperimeter()
methods, then it also implements theshape
interface. - Likewise, a single type can implement multiple interfaces as well. For example, an empty interface like
interface {}
is always implemented by every type. - In Go, interfaces are implemented implicitly. If you check the above function, you can see that we have never explicitly mentioned that the
rectangle
struct is implemented byshape
(like we do in Java).
Named argument interfaces
You can also use named arguments and return data in interface methods. If you check the below function, it is not that clear what are the arguments passing to the Copy()
method. You only know that they should be strings.
type Copier interface {
Copy(string, string) int
}
Therefore, to give more readable interface methods, you can define the interface like below.
type Copier interface {
Copy(sourceFile string, destFile string) (copiedBytes int)
}
Type assertion
Assume you have several types implemented by the shape
interface. But you need to write a function to take the area of a rectangle
. In such a case, you should use type assertion.
func getArea(s shape) int {
r, ok := s.(rectangle)
if ok {
return r.length * r.width
}
return 0
}
Here, you pass a shape and check whether it's a rectangle. If it is, the value of ok
would be true
. And r
would get a rectangle struct.
Type Switch
Instead of type assertion, you can also use switch cases.
func printNumericValue(num interface{}) {
switch v := num.(type) {
case int:
fmt.Printf("%d", v)
case string:
fmt.Printf("%s", v)
default:
fmt.Printf("%T", v)
}
}
Clean Interfaces
- Keep interfaces as simple as you can.
- Don't make the interface aware of the underlying types (ex:
shape
interface should not have methods likeisCircle()
).
Interfaces are not classes. They are much simpler. They don't have constructors or deconstructors to create/destroy data. There is no hierarchy of passing behavior (parent-to-child behavior).