Sunday 11 April 2021

Interfaces in Go

 This is a study note from YouTube tutorial: https://www.youtube.com/watch?v=YS4e4q9oBaU&t=12098s

Interface is a collection of function prototypes. You can define interface like below:

type Writer interface {
Write([]byte) (int, error)
}

Interfaces should be implemented as methods.

type ConsoleWriter struct{}

func (cw ConsoleWriter) Write(data []byte) (int, error) {
n, err := fmt.Println(string(data))
return n, err
}

Polymorphism

You can use interface to realise polymorphism like below:

func main() {
var w Writer = ConsoleWriter{} // Here can use different structs
//with different implementation
w.Write([]byte("Hello go!"))

}

Implicit implementation

Except polymorphism, you can use interface to do implicit implementation. What does implicit implementation means? Since you don't need to use "implements" keyword to implement an interface like JAVA and some other languages, interfaces decouple the definitions from their implementations. There are a lot of packages which utilise structs with fields and methods. So you can design your own interfaces which can utilise the methods in the structs.

Use any type to implement interfaces

You don't have to use struct to implement interfaces. In fact, you can use any type to implement interfaces.

func main() {
myInt := IntCounter(0)
var inc Incrementer = &myInt
for i := 0; i < 10; i++ {
fmt.Println(inc.Increment())
}
}

type Incrementer interface { // define interface
Increment() int
}

type IntCounter int // define int type

func (ic *IntCounter) Increment() int { // implement interface
*ic++ // Here is important, it increment the type itself which is int
return int(*ic)
}

Interface embedding

You can embed interface into another interface like structs. Below is a writer example code from the tutorial video.

func main() {
var wc WriterCloser = NewBufferredWriterCloser()
wc.Write([]byte("Hello Go developers, this is a test"))
wc.Close()
}

type Writer interface {
Write([]byte) (int, error)
}

type Closer interface {
Close() error
}

type WriterCloser interface {
Writer // Here two interfaces are
Closer // embedded in composer interface
}

type BufferedWriterCloser struct {
buffer *bytes.Buffer
}

func (bwc *BufferedWriterCloser) Write(data []byte) (int, error) {
n, err := bwc.buffer.Write(data)
if err != nil {
return 0, err
}
v := make([]byte, 8)
for bwc.buffer.Len() > 8 {
_, err := bwc.buffer.Read(v)
if err != nil {
return 0, err
}
_, err = fmt.Println(string(v))
if err != nil {
return 0, err
}

}
return n, nil
}

func (bwc *BufferedWriterCloser) Close() error {
for bwc.buffer.Len() > 0 {
data := bwc.buffer.Next(8)
_, err := fmt.Println(string(data))
if err != nil {
return err
}
}
return nil
}

func NewBufferredWriterCloser() *BufferedWriterCloser {
return &BufferedWriterCloser{
buffer: bytes.NewBuffer([]byte{}),
}
}

Interface type conversion

Interface variable can be converted to the value type (structs or other types) using dot oeprtation.

func main() {
var wc WriterCloser = NewBufferredWriterCloser()
wc.Write([]byte("Hello Go developers, this is a test"))
//wc.Close()

bwc := wc.(*BufferedWriterCloser) // Here wc is converted to
// to struct directly, in
// this way you can access
// the buffer field directly
fmt.Println(bwc.buffer)
}

You can check the if the conversion is successful or failed by checking the second return value from the conversion operation.

func main() {
r, ok := wc.(io.Reader)
if ok {
fmt.Println(r)
} else {
fmt.Println("Conversion failed")
}
}

The result is:
Conversion failed

Empty interface and type conversion

Anything in Go can be an empty interface. This is useful when you want to get a value and then later want to covert it onto others. Because empty interface has no method in it. So in order to use it, you have to do a type conversion.
func main() {
var myObj interface{} = NewBufferredWriterCloser()
if wc, ok := myObj.(io.WriteCloser); ok { // Here do convertion
wc.Write([]byte("Hello Go developers, this is a test"))
wc.Close()
}

r, ok := myObj.(io.Reader)
if ok {
fmt.Println(r)
} else {
fmt.Println("Conversion failed")
}
}

Type switch

You can use switch to check the type of a variable.
func main() {
var i interface{} = 0
switch i.(type) {
case int:
fmt.Println("i is an integer")
case string:
fmt.Println("i is a string")
default:
fmt.Println("I don't know what it is")

}
}





References: https://www.youtube.com/watch?v=YS4e4q9oBaU&t=12098s

No comments:

Post a Comment

Difference between "docker stop" and "docker kill"

 To stop a running container, you can use either "docker stop" or "docker kill" command to do so. Although it seems doin...