What is an Interface?
An interface defines a set of methods without their implementation. Also an interface cannot contains variables. An interface is declared in the format:
type Namer interface {
Method(params) return type
}
Namer is a interface type. The name of an interface is formed by the method name plus the [er] suffix, such as Logger, Converter, Writer, and so on.
Go interfaces can have values that are a variable of the interface type or an interface value.
The interface variable both contains the value of the receiver value and a pointer to the appropriate method in a method table.
var anI Namer
An Example of Interface:
package main
import "fmt"
type Shaper interface {
Area() float32
}
type Sqaure struct {
side float32
}
func (sq *Square) Area(){
return sq.side * sq.side
}
func main(){
square := new(Square)
square.side = 10
areaInterface := Shaper(square)
fmt.Printf("The square has area: %f\n", areaInterface.Area())
}
In the program above, we define an interface Shaper, with one method Area() returning float32 value and a struct of type Square, with one field side of type float32. We define a method Area() that can be called by a pointer to the Square type object. This method returns the area of a square sq. The struct Square implements the interface Shaper. Now, the interface variable contains a reference to the Square variable, and through it, we can call the method Area() on Square.
An Example from the standard library:
type Reader interface {
Read(p []byte) (n int, err error)
}
If we define a variable reader like this:
var reader io.Reader
// then the following is correct code
reader = os.Stdin
reader = bufio.NewReader(r)
reader = new(bytes.Buffer)
f,_ := os.Open("test.txt")
reader = bufio.NewReader(f)
Practice Time:
package main
import "fmt"
type Simpler interface {
Get() int
Set(n int)
}
type Simple struct {
n int
}
func (si *Simple) Get() int {
return si.n
}
func (si *Simple) Set(n int) {
si.n = n
}
func FI(it Simpler) int {
it.Set(10)
return it.Get()
}
func main() {
fmt.Println("Interface")
s := new(Simple)
inF := Simpler(s)
fmt.Println(FI(inF))
// shorthand
var sh Simple
fmt.Println(FI(&sh))
}
What I have done in the above code:
Define an interface Simpler
with methods Get()
, which returns an integer, and Set()
which has an integer as a parameter. Make a struct type Simple
, which implements this interface.
Then, define a function that takes a parameter of the type Simpler
and calls both methods upon it. Call this function from main
to see if it all works correctly.
Embedded Interface and Type Assertions
An interface
can contain the name of one (or more) interface(s), which is equivalent to explicitly enumerating the methods of the embedded interface in the containing interface.
type ReadWrite interface {
Read(b Buffer) bool
Write(b Buffer) bool
}
type Lock interface {
Lock()
Unlock()
}
type File interface {
ReadWrite
Lock
Close()
}
Detecting and converting the type of an interface variable
An interface type variable varI
can contain a value of any type; we must have a means to detect this dynamic type, which is the actual type of the value stored in the variable at run time. The dynamic type may vary during execution but is always assignable to the type of the interface variable itself.
In general, we can test if varI
contains at a certain moment a variable of type T
with the type assertion test:
// unchecked type assertion
v := varI.(T)
// checked type assertion
if v, ok := varI.(T); ok {
Process(v)
return
}else{
// here varI is not of type T
}
Note: Always use the
comma
,ok
form for type assertions
if v, ok := varI.(T); ok {
// ...
}
Type Assertions:
package main
import (
"fmt"
"math"
)
type Shaper interface {
Area() float32
}
type Square struct {
side float32
}
func (s *Square) Area() float32 {
return s.side * s.side
}
func (ci *Circle) Area() float32 {
return ci.radius * ci.radius * math.Pi
}
type Circle struct {
radius float32
}
func main() {
var aInf Shaper
square := new(Square)
// circle := new(Circle)
square.side = 5
// circle.radius = 10
aInf = square
// useing if, comma, ok
if t, ok := aInf.(*Square); ok {
fmt.Printf("The type of aInf is: %T\n", t)
}
if t, ok := aInf.(*Circle); ok {
fmt.Printf("The type of aInf is: %T\n", t)
} else {
fmt.Println("aInf does not contain a variable of type Circle")
}
// using type switch
switch t := aInf.(type) {
case *Square:
fmt.Printf("Type Square %T with value %v\n", t, t)
case *Circle:
fmt.Printf("Type Circle %T with value %v\n", t, t)
default:
fmt.Printf("Unexpected type %T", t)
}
fmt.Printf("The square has area: %f\n", aInf.Area())
}
Type assertion Example
package main
import "fmt"
type Simpler interface {
Get() int
Set(n int)
}
type Simple struct {
n int
}
func (p *Simple) Get() int {
return p.n
}
func (p *Simple) Set(u int) {
p.n = u
}
type RSimple struct {
n int
}
func (p *RSimple) Get() int {
return p.n
}
func (p *RSimple) Set(u int) {
p.n = u
}
func FI(it Simpler) int {
switch it.(type) {
case *Simple:
it.Set(15)
return it.Get()
case *RSimple:
it.Set(10)
return it.Get()
}
return 0
}
func main(){
var simpler Simple
var rSimple RSimple
// &simple is required because Get() is defined with the type pointer as receiver
fmt.Println(FI(&simple))
// &rSimple is required because Get() is defined with the type pointer as receiver
fmt.Println(FI(&rSimple))
}
Thanks for Reading🌹
If you'd like to keep in touch with me, follow me on Twitter! DMs are open.
Top comments (0)