Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are interfaces needed in Golang?

In Golang, we use structs with receiver methods. everything is perfect up to here.
I'm not sure what interfaces are, however. We define methods in structs and if we want to implement a method on a struct, we write it anyway again under another struct.
This means that interfaces seem to be just method definitions, taking just extra unneeded space on our page.

Is there any example explaining why I need an interface?

like image 939
nikoss Avatar asked Aug 23 '16 05:08

nikoss


People also ask

What is the purpose of using interfaces?

You use an interface to define a protocol of behavior that can be implemented by any class anywhere in the class hierarchy. Interfaces are useful for the following: Capturing similarities among unrelated classes without artificially forcing a class relationship.

How do interfaces work in Golang?

An interface in Go is a type defined using a set of method signatures. The interface defines the behavior for similar type of objects. An interface is declared using the type keyword, followed by the name of the interface and the keyword interface . Then, we specify a set of method signatures inside curly braces.

What's an interface in Go?

In Go, an interface is a set of method signatures. When a type provides definition for all the methods in the interface, it is said to implement the interface. It is much similar to the OOP world. Interface specifies what methods a type should have and the type decides how to implement these methods.

What is the difference between struct and interface in Golang?

Structs and interfaces are Go's way of organizing methods and data handling. Where structs define the fields of an object, like a Person's first and last name. The interfaces define the methods; e.g. formatting and returning a Person's full name.


2 Answers

Interfaces are too big of a topic to give an all-depth answer here, but some things to make their use clear.

Interfaces are a tool. Whether you use them or not is up to you, but they can make code clearer, shorter, more readable, and they can provide a nice API between packages, or clients (users) and servers (providers).

Yes, you can create your own struct type, and you can "attach" methods to it, for example:

type Cat struct{}

func (c Cat) Say() string { return "meow" }

type Dog struct{}

func (d Dog) Say() string { return "woof" }

func main() {
    c := Cat{}
    fmt.Println("Cat says:", c.Say())
    d := Dog{}
    fmt.Println("Dog says:", d.Say())
}

We can already see some repetition in the code above: when making both Cat and Dog say something. Can we handle both as the same kind of entity, as animal? Not really. Sure we could handle both as interface{}, but if we do so, we can't call their Say() method because a value of type interface{} does not define any methods.

There is some similarity in both of the above types: both have a method Say() with the same signature (parameters and result types). We can capture this with an interface:

type Sayer interface {
    Say() string
}

The interface contains only the signatures of the methods, but not their implementation.

Note that in Go a type implicitly implements an interface if its method set is a superset of the interface. There is no declaration of the intent. What does this mean? Our previous Cat and Dog types already implement this Sayer interface even though this interface definition didn't even exist when we wrote them earlier, and we didn't touch them to mark them or something. They just do.

Interfaces specify behavior. A type that implements an interface means that type has all the methods the interface "prescribes".

Since both implement Sayer, we can handle both as a value of Sayer, they have this in common. See how we can handle both in unity:

animals := []Sayer{c, d}
for _, a := range animals {
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}

(That reflect part is only to get the type name, don't make much of it as of now.)

The important part is that we could handle both Cat and Dog as the same kind (an interface type), and work with them / use them. If you were quickly on to create additional types with a Say() method, they could line up beside Cat and Dog:

type Horse struct{}

func (h Horse) Say() string { return "neigh" }

animals = append(animals, Horse{})
for _, a := range animals {
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}

Let's say you want to write other code that works with these types. A helper function:

func MakeCatTalk(c Cat) {
    fmt.Println("Cat says:", c.Say())
}

Yes, the above function works with Cat and with nothing else. If you'd want something similar, you'd have to write it for each type. Needless to say how bad this is.

Yes, you could write it to take an argument of interface{}, and use type assertion or type switches, which would reduce the number of helper functions, but still looks really ugly.

The solution? Yes, interfaces. Simply declare the function to take a value of an interface type which defines the behavior you want to do with it, and that's all:

func MakeTalk(s Sayer) {
    fmt.Println(reflect.TypeOf(s).Name(), "says:", s.Say())
}

You can call this function with a value of Cat, Dog, Horse or any other type not known until now, that has a Say() method. Cool.

Try these examples on the Go Playground.

like image 66
icza Avatar answered Oct 19 '22 10:10

icza


interface provide some kinds of generics. Think about duck typing.

type Reader interface{
     Read()
}

func callRead(r Reader){
      r.Read()
}

type A struct{
}
func(_ A)Read(){
}

type B struct{
}
func(_ B)Read(){
}

It's ok to pass struct A, and B to callRead, because both implement Reader interface. But if without interface, we should write two function for A and B.

func callRead(a A){
     a.Read()
}

func callRead2(b B){
     b.Read()
}
like image 17
zzn Avatar answered Oct 19 '22 10:10

zzn