一些 Go 接口的示例是什么?

7

我发现了一篇关于Go语言的有趣博客文章

我正在尝试理解接口的概念,但是从博客文章中的代码片段中很难做到这一点,而从语言规范中几乎是不可能的。

有人能指出一个在工作程序中使用Go接口的简单示例吗?


以下是一些可能有用的资源:https://gobyexample.com/interfaces + http://www.laktek.com/2012/02/13/learning-go-interfaces-reflections/ + http://golangtutorials.blogspot.com.au/2011/06/interfaces-in-go.html - elithrar
Go接口的例子?解耦代码。请参见https://dev59.com/RWXWa4cB1Zd3GeqPIweV#62297796。动态调用方法。请参见https://dev59.com/xmw05IYBdhLWcg3wxUj_#62336440。访问Go包。请参见https://dev59.com/fErSa4cB1Zd3GeqPVVA8#62278078。将任何值分配给变量。请参见https://dev59.com/XWAg5IYBdhLWcg3w0Nsz#62337836。 - user12817546
6个回答

4
这是一个正在进行的学习练习,当然不是好样式的好例子,但 在这里规范)。
此外,作为一个更为奇特的例子,我在 go-nuts 邮件列表上发了一篇关于使用 interface{} 构建处理匿名数据的函数(在这种情况下,是“三元操作”函数)的文章:a post
package main
import "fmt";
func Tern(exp bool, a interface{}, b interface{}) (interface{}) {
    if exp { return a }
    return b
}
func main() {
    a := 7; b := 1;
    result := Tern(a > b, a, b);
    fmt.Printf("%d\n", result);
}

谢谢,我会看一下你的例子。我想要一段可编译的代码来使用,这看起来不错。 - user181548
这里的 interface{} 是什么意思? - user181548
"interface{}" 是一个空接口,也就是说它可以匹配所有类型。可以将其视为“任意”值:一种编写通用函数以操作任何值的方法。不幸的是,文档在这方面缺乏;但是,结合“反射”包,您可以获得一些非常有趣的类型操作选项。 - esm
我正在查看有关reflect的一些库代码,但最终有点困惑。它在Printf例程中经常使用,其中变量的类型是从变量本身获取的。但我在一个错综复杂的依赖迷宫中迷失了方向。 - user181548
如果将“interface”替换为“int”,则此示例可以正常工作。请参见https://play.golang.org/p/ww-5ma7m5IS。 - user12817546

4

这篇名为“在Go中的接口 - 第二部分:帮助适应性、进化式设计”(2012年1月,来自Sathish VJ)的教程清楚地提到了Go接口的主要优势:

Go的接口不是Java或C#接口的变体,它们更加强大。
它们是大规模编程和适应性、进化式设计的关键

请看同一篇文章中有关巴士不同视角(接口)的示例:

package main

import "fmt"

//Go Step 1: Define your data structures
type Bus struct {
    l, b, h int
    rows, seatsPerRow int
}

//Go Step 2: Define a real world abstraction that could use the data we structure we have
type Cuboider interface {
    CubicVolume() int
}

//Go Step 3: Implement methods to work on data
func (bus Bus) CubicVolume() int {
    return bus.l *  bus.b * bus.h
}

//Go step - repeat 2 & 3 for any other interfaces
type PublicTransporter interface  {
    PassengerCapacity() int
}

func (bus Bus) PassengerCapacity() int {
    return bus.rows * bus.seatsPerRow
}

func main() {
    b := Bus{
             l:10, b:6, h:3,
             rows:10, seatsPerRow:5}

    fmt.Println("Cubic volume of bus:", b.CubicVolume())
    fmt.Println("Maximum number of passengers:", b.PassengerCapacity())
}

它似乎是以数据为中心的-首先定义您的数据,然后随着界面抽象的构建而进行。
在这里,层次结构是通过“顺带而来”地构建的,没有明确说明-根据与类型相关联的方法签名,它被理解为实现特定的接口。
现在让我们假设随着时间的推移,我们公交车项目的一些要求发生了变化-现在有一个新法律规定每个乘客至少应该有一定的立方体体积。
我们的公交车现在必须遵守一个称为PersonalSpaceLaw的新接口,它不同于它已经实现的任何其他接口。
//new requirement that the Bus must be compatible with
type PersonalSpaceLaw interface {
    IsCompliantWithLaw() bool
}

func (b Bus) IsCompliantWithLaw() bool {
    return (b.l * b.b * b.h) / (b.rows * b.seatsPerRow) >= 3
}

这个功能已经扩展,而没有改变核心类或核心层次结构。这种实现更加简洁、易于扩展,并且可以随着项目需求的变化而更好地扩展。

这里是在Go Playground中完整工作程序的链接

文章以John Asmuth在关于Go接口生产力的讨论串中的引用结束:

“事实上,我不必花时间设计某种类型层次结构,然后再重复两三次才能完成。它甚至不仅仅容易做对——事实上,我只需要专注于实际算法,而不必担心其他问题。

我知道你有一段时间之前发布了这个问题,但是这个例子非常好,所以谢谢你! - Xeoncross
非常好的例子,解释和引用。+1 - phw
1
不确定这是任何示例。这里是相同的代码,其中接口被删除,正常工作:https://play.golang.org/p/WXpl78MAQrV - Vadym Tyemirov
@VonC 对于先定义数据再构建接口的高水平回答,我点赞并引用了你的回答。请参见 https://dev59.com/RWXWa4cB1Zd3GeqPIweV#62297796。 - user12817546
1
@TomJ 我也喜欢 https://lbry.tv/@dfeury:5/Interfaces-in-Go-are-SO-GOOD---Go---Golang-Interface-Tutorial-_--feurious:5 和 https://twitter.com/davecheney/status/942593128355192832?s=19。 - VonC

2
package main

type Stringer interface {
    String() string
}

type pie int
type pizza string

func (p pie) String() string{
    return "pie"
}

func (p pizza) String() string{
    return "pizza"
}

func main(){
    var a pie
    var b pizza
    fmt.Println(a,b) //fmt.Println() will look for Stringers and call their String() method.
}

1
扩展 @Jessta 的优秀示例。提供了一个使用 Go 接口访问 Go 标准库的工作程序的简单示例。
package main

import (
    "encoding/json"
    . "fmt"
)

func main() {
    var i interface{} = c

    e := func() error { return c } // type error interface { Error() string}
    Println(e())                   // Hiss

    s := func() Stringer { return d } // type Stringer interface {String() string}

    // func Println(a ...interface{}) (n int, err error)
    Println(s()) // Woof

    d := `{"Pet":"Dog","Age":2, "Eat": "Bone"}`
    json.Unmarshal([]byte(d), &i) // func Unmarshal(data []byte, v interface{}) error
    m := i.(map[string]interface{})
    Println(m["Age"]) // 2
}

type cat string
type dog string
var c cat
var d dog
func (cat) Error() string { return "Hiss" }
func (dog) String() string { return "Woof" }

接口是Go语言最独特和强大的特性。它们对库设计有深远影响,使得真正的组件体系结构成为可能。其中一个最好的例子是io.Reader和io.Writer,它们是Unix管道思想的泛化。请参见https://talks.golang.org/2015/simplicity-is-complicated.slide
按照惯例,错误类型为error,它是一个简单的内置接口。请参见https://golang.org/doc/effective_go.html#errorshttps://talks.golang.org/2012/splash.article。 错误变量表示任何可以将自己描述为字符串的值。func() error { return c } 调用 type error interface { Error() string}func (cat) Error() string 实现了type error interface { Error() string}。请参见https://blog.golang.org/error-handling-and-go
一个Stringer可以很好地打印自己。任何实现了String的都是Stringer。如果参数是Stringer,则fmt.Println调用String方法。参见https://talks.golang.org/2013/go4python.slide#33func() Stringer { return d }调用type Stringer interface {String() string}func (dog) String() string实现了type Stringer interface {String() string}fmt.Println的签名是func Println(format string, a ...interface{}) (n int, err error),这意味着它的参数(在格式字符串之后)是接口值。参见https://blog.golang.org/constants。引号已编辑以匹配示例。

标准库中广泛使用的关键接口使得将API链接在一起变得容易。请参见https://talks.golang.org/2012/goforc.slide#46。更多示例请参见https://talks.golang.org/2014/go4gophershttps://talks.golang.org/2014/go4gophers.slide#1


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

0
在Go语言中,接口的基本概念是实现了接口定义的方法的任何对象都可以成为该接口的一部分。
最好的例子是Writer接口。Rob Pike在Google Tech Talk的介绍演讲中有一个例子(http://www.youtube.com/watch?v=rKnDgT73v8s)- 在演讲中向前滚动到33:25处听他的解释。

0

截至2018年,Python 3中唯一的示例是这个。 - Vadym Tyemirov

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