How to use interfaces in Go

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Before I started programming Go, I was doing the most of my work with Python. As a Python programmer, I found that learning to use interfaces on Go was extremely difficult. That's, the basics were easy, and I knew the "the interfaces in the" the standard library, but it took some practice BEF Ore I knew how to design my own interfaces. In this post, I'll discuss Go's type system in a effort to explain what to use interfaces effectively.

Introduction to Interfaces
So what's an interface? An interface is the Things:it is a set of methods and it is also a type. Let's focus on the method set aspect of interfaces first.

Typically, we ' re introduced to interfaces with some contrived example. Let's go with the contrived example of writing some application where you ' re defining Animal datatypes, because that ' s a T Otally realistic situation that happens all the time. The Animal type would be a interface, and we'll define an Animal as being anything that can speak. This was a core concept in Go ' type system; Instead of designing our abstractions in terms of that kind of data our types can hold, we design our abstractions in term s of what the actions our types can execute.

We start by defining our Animal interface:

type Animal interface {    Speak() string}

Pretty simple:we define an Animal as being any type this has a method named Speak. The Speak method takes no arguments and returns a string. Any of the type that defines this method was said to satisfy the Animal interface. There is no implements keyword in Go; Whether or not a type satisfies an interface is determined automatically. Let's create a couple of types that satisfy this interface:

typeDogstruct{}func(d Dog) Speak ()string{return "woof!"}typeCatstruct{}func(c Cat) Speak ()string{return "meow!"}typeLlamastruct{}func(l Llama) Speak ()string{return "?????"}typeJavaprogrammerstruct{}func(J Javaprogrammer) Speak ()string{return "Design patterns!"}

We now had four different types of animals:a dog, a cat, a llama, and a Java programmer. In our main () function, we can create a slice of Animals, and put the one of each type into the that slice, and see what each Anim Al says. Let's do it now:

func main() {    animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}    forrange animals {        fmt.Println(animal.Speak())    }}

You can view and run this example HERE:HTTP://PLAY.GOLANG.ORG/P/YGTD4MTGD5

Great, now what do you know the interfaces, and I don ' t need to the talk about them any more, right? Well, no, not really. Let's look at a few things that aren ' t very obvious to the budding gopher.

The interface{} type
The interface{} type, the empty interface, is the source of much confusion. The interface{} type is the interface, which has no methods. Since There is no implements keyword, all types implement at least zero methods, and satisfying an interface are done Autom atically, all types satisfy the empty interface. That means so if you write a function that takes an interface{} value as a parameter and you can supply that function with Any value. So, the This function:

func DoSomething(v interface{}) {   ...}

Would accept any parameter whatsoever.

Here's where it gets confusing:inside of the dosomething function, what is V ' s type? Beginner Gophers is the led to believe the "V is of any type", and that's is wrong. V is isn't of any type; It is of interface{} type. Wait, what? When passing a value into the DoSomething function, the Go runtime would perform a type conversion (if necessary), and Conv ERT the value to an interface{} value. All values has exactly one type at runtime, and V's one static type is interface{}.

This should leave-wondering:ok, so if a conversion are taking place, what's actually being passed into a function That takes a interface{} value (or, what's actually stored in an []animal slice)? An interface value is constructed of, words of data; One word is used to point-a method table for the value's underlying type, and the other word was used to point to the AC Tual data being held by that value. I don ' t want to bleat in about this endlessly. If you understand this interface value is both words wide and it contains a pointer to the underlying data, that ' s typic Ally enough to avoid common pitfalls. If you're curious to learn more about the implementation of interfaces, I think Russ Cox's description of interfaces is V ery, very helpful.

In our previous example, when we constructed a slice of Animal values, we do not have the say something onerous like Anima L (dog{}) to put a value of type Dog into the slice of Animal values, because the conversion is handled for US automatical Ly. Within the animals slice, each element is of a Animal type, but we different values have different underlying types.

So ... why does this matter? Well, understanding how interfaces is represented in memory makes some potentially confusing things very obvious. For example, the question ' can I convert a []t to an []interface{} ' is easy to answer once ' understand how interfaces a Re represented in memory. Here's an example of some broken code which is representative of a common misunderstanding of the interface{} type:

package mainimport (    "fmt")func PrintAll(vals []interface{}) {    forrange vals {        fmt.Println(val)    }}func main() {    names := []string{"stanley""david""oscar"}    PrintAll(names)}

Run it Here:http://play.golang.org/p/4duboi2hju

By running this, you can see that we encounter the following error:cannot use names (Type []string) as type []INTERFAC e {} in function argument. If we want to actually make this work, we would has the to convert the []string to an []interface{}:

package  Main import  ( "FMT" ) func  Printall (Vals []interface  {}) {for  _, Val: = Span class= "Hljs-keyword" >range  vals {fmt. Println (val)}}func  Main () {names: = []string  {  "Stanley" ,  "David" ,  "Oscar" } vals: = make  ([]interface  {}, Span class= "hljs-built_in" >len  (names)) for  i, V: = range  names {vals[i] = v} printall (Vals)}  

Run it HERE:HTTP://PLAY.GOLANG.ORG/P/DHG1YS6BJS

That's pretty ugly, but C ' est la vie. Not everything is perfect. (In reality, this doesn ' t come up very often, because []interface{} turns off to being less useful than you would initially E Xpect)

Pointers and interfaces
Another subtlety of interfaces is a interface definition does not prescribe whether an implementor should implement The interface using a pointer receiver or a value receiver. When you're given an interface value, there's no guarantee whether the underlying type is or isn ' t a pointer. In our previous example, we defined all of our methods on value receivers, and we put the associated values into the Anima L Slice. Let's change this and make the Cat's Speak () method take a pointer receiver:

funcstring {    return"Meow!"}

If you change this one signature, and you try to run the same program exactly As-is (Http://play.golang.org/p/TvR758rfre), You'll see the following error:

Prog.go:40:cannot use cat literal (type cat) as type Animal in array element:
Cat does not implement Animal (Speak method requires pointer receiver)
This error message was a bit confusing at first and to being honest. What it's saying isn't that the the interface Animal demands so you define your method as a pointer receiver The tried to convert a Cat struct into an Animal interface value, but only *cat satisfies that interface. You can fix this bug by passing in a *cat pointer to the Animal slice instead of a Cat value, by using new (Cat) instead of Cat{} (could also say &cat{}, I simply prefer the look of new (Cat)):

animals := []Animal{Dog{}, new(Cat), Llama{}, JavaProgrammer{}}

Now we program works AGAIN:HTTP://PLAY.GOLANG.ORG/P/X5VWYEXXBM

Let's go in the opposite Direction:let's pass in a *dog pointer instead of a DOG value, but this time we won ' t change the Definition of the Dog type ' s Speak method:

animals := []Animal{newnew(Cat), Llama{}, JavaProgrammer{}}

This also works (HTTP://PLAY.GOLANG.ORG/P/UZ618QBPKJ), but recognize a subtle difference:we didn ' t need to change the Typ E of the receiver of the Speak method. This works because a pointer type can access the methods of it associated value type, but not vice versa. That's, a *dog value can utilize the Speak method defined on Dog, but as we saw earlier, a Cat value cannot access the Sp Eak method defined on *cat.

That could sound cryptic, but it makes sense if you remember the following:everything in Go was passed by value. Every time call a function, the data you ' re passing to it is copied. In the-a method with a value receiver, the value is copied when calling the method. This was slightly more obvious if you understand that a method of the following signature:

T)MyMethod(s string) {    ...}

is a function of type Func (T, String); Method receivers is passed into the function by value just like any other parameter.

Any changes to the receiver made inside of a method defined on a value type (e.g., func (d Dog) Speak () {...}) would not Be seen by the caller because the caller is scoping a completely separate Dog value. Since everything is passed by value, it should was obvious why a *cat method was not usable by a Cat value; Any one Cat value may has any number of *cat pointers. If we try to call a *cat method by using a-Cat value, we never had a *cat pointer to begin with. Conversely, if we have a method on the Dog type, and we had a *dog pointer, we know exactly which Dog value to use when C Alling This method, because the *dog pointer points to exactly one Dog value; The Go runtime would dereference the pointer to their associated DOG value any time it was necessary. That's, given a *dog value D and a method Speak on the Dog type, we can just say D.speak (); We don ' t need to say something like D->speak () as we might does in other languages.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.