Go中接口的用法

17

我希望能通过一个简单的示例来了解Go语言中接口类型的使用。

我已经阅读了网络文档,但是仍然不理解。


你有在文档中读到这个吗?http://golang.org/doc/effective_go.html#interfaces_and_types。还可以尝试这个http://diveintogo.blogspot.com/2010/03/classic-inheritance-in-go-lang.html。 - Justin
1
是的,我已经阅读了文档...感谢提供链接。 - Speccy
在Go中使用接口?解耦代码。请参见https://dev59.com/RWXWa4cB1Zd3GeqPIweV#62297796。动态调用方法。请参见https://dev59.com/xmw05IYBdhLWcg3wxUj_#62336440。访问Go包。请参见https://dev59.com/fErSa4cB1Zd3GeqPVVA8#62278078。将任何值分配给变量。请参见https://dev59.com/XWAg5IYBdhLWcg3w0Nsz#62337836。 - user12817546
4个回答

49

Go接口背后的理念是鸭子类型。简而言之:如果你看起来像一只鸭子,叫声像一只鸭子,那么你就是一只鸭子。这意味着,如果您的对象实现了所有鸭子的特征,那么使用它作为鸭子应该是没有问题的。以下是一个例子:

package main

import (
    "fmt"
)

type Walker interface {
    Walk() string
}

type Human string
type Dog string

func (human Human) Walk() string { //A human is a walker
    return "I'm a man and I walked!"
}

func (dog Dog) Walk() string { //A dog is a walker
    return "I'm a dog and I walked!"
}

//Make a walker walk
func MakeWalk(w Walker) {
    fmt.Println(w.Walk())
}

func main() {
    var human Human
    var dog Dog
    MakeWalk(human)
    MakeWalk(dog)
}

在这里,一个Human是一个Walker,而一个Dog也是一个Walker。为什么?因为他们都......嗯......走路。它们都实现了Walk() string函数。因此,你可以在它们身上执行MakeWalk

当你希望不同类型以相同的方式行为时,这非常有用。一个实际的例子是文件类型对象(套接字、文件对象)-你需要一个Write和一个Read函数。然后你可以以相同的方式使用Write和Read来独立于它们的类型-这很酷。


8
谢谢!这真的是我想知道的。比Go教程中的要清晰得多。 - Speccy
在Golang中,接口有哪些好处?如果我正在为2个对象使用接口,并且我的接口有3个方法,是否意味着我的2个对象都应该实现这3个方法? - Avdept
@Avdept 是的,您必须为您的类型实现与接口相同的签名。不确定您所说的“好处”是什么意思。 - Alex Plugaru
虽然将Go解释为具有鸭子类型可以帮助人们理解Go的接口,但实际上Go并不使用鸭子类型,这是动态语言可能具有的一种语言特性。由于Go是静态类型语言,它使用结构类型,正如OP链接的维基百科文章的一个部分所解释的那样。 #只是提供信息 #顺便说一句 - undefined

2

这是另一个工作示例,展示了接口和结构之间的交互

package main

import "fmt"

type Info interface {
Noofchar() int
Increment()
}

type Testinfo struct {
noofchar int     
}

func (x *Testinfo) Noofchar() int {
return x.noofchar
}
func (x *Testinfo) Increment() {
x.noofchar++
}

func main(){
var t Info = &Testinfo{noofchar:1}
fmt.Println("No of char ",t.Noofchar())
t.Increment()
fmt.Println("No of char ",t.Noofchar())
}

@Speccy 我不得不谷歌一下那个术语。对于任何想知道的人,它的意思是魔术师/巫师。 - yash

0

在@AlexPlugaru和https://stackoverflow.com/a/18854285/12817546的卓越答案之外,还要补充@NickCraig-Wood的意见。

package main

import . "fmt"

func main() {
    cat("Bird").Eat()      // Bird
    eater.Eat(cat("Milk")) // Milk
    Break(cat("Fish"))     // Fish
    Lunch().Eat()          // Mice
    Tea(true)           // Bird
}

type cat string
type eater interface{ Eat() }

func (c cat) Eat()  { Println(c) }
func Break(e eater) { e.Eat() }
func Lunch() eater  { return cat("Mice") }
func Tea(b bool) {
    if b {
        cat("Bird").Eat()
    }
}

结构体或任何其他具体类型上的方法是静态解析的。请参见cat("Bird").Eat()eater.Eat(cat("Milk"))。唯一具有动态分派方法的方式是通过接口。请参见Break(cat("Fish"))Lunch().Eat()http://www.golangbr.org/doc/faq#How_do_I_get_dynamic_dispatch_of_methods

Tea(true)调用一个方法而不使用接口进行动态调度。请参见https://talks.golang.org/2014/taste.slide#19。但是,接口-按设计和约定-鼓励我们编写可组合的代码。请参见https://talks.golang.org/2014/go4gophers.slide#21

结果:简单的部分通过小接口连接在一起。请参见https://talks.golang.org/2012/chat.slide#5。最好有许多小而简单的东西,而不是一个大而复杂的东西。请参见https://talks.golang.org/2014/go4gophers.slide#24
您还可以将任何内容隐藏在私有包中,只公开特定类型、接口和工厂函数。请参见Break(cat("Fish"))Lunch().Eat()https://code.tutsplus.com/tutorials/lets-go-object-oriented-programming-in-golang--cms-26540

使用接口解耦代码。请参见 https://dev59.com/RWXWa4cB1Zd3GeqPIweV#62297796。动态调用方法。请参见 https://dev59.com/xmw05IYBdhLWcg3wxUj_#62336440。访问Go包。请参见 https://dev59.com/fErSa4cB1Zd3GeqPVVA8#62278078。将任何值分配给变量。请参见 https://dev59.com/XWAg5IYBdhLWcg3w0Nsz#62337836。 - user12817546

0
在这个例子中,我使用接口来展示如何在Golang中实现多态性。
package main 


import(
    "fmt"
    "math"
)

func main(){
    rect := Rectangle{20,50}
    cir := Circle{2}

    //According to object you passed in getArea method, 
    // it will change the behaviour and that is called Polymorphism.
    fmt.Println("Area of Rectangle =",getArea(rect))
    fmt.Println("Area of Circle =",getArea(cir))

}

//Interface Shape with one area method
type Shape interface{
     area() float64
}

//Creating Rectangle and Circle type using struct 
type Rectangle struct{
    height float64
    width float64
}

type Circle struct{
    radius float64
}

//Receiver function, which implements struct's area methods
func(r Rectangle) area() float64{
    return r.height * r.width
}

func(c Circle) area() float64{
    return math.Pi * math.Pow(c.radius,2)
}

//passing interface as arguments, which can calculate shape of any mentioned type
//All the struct are tied together because of the Interface.
func getArea(shape Shape) float64{
    return shape.area()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接