英文:
If Go's interfaces aren't enforced, are they necessary?
问题
Go语言具有接口类型作为特性,类似于C风格的接口。然而,Go的接口类型似乎并没有被强制执行 - 它们仅仅定义了协议,而没有实际应用于类型。既然它们没有被强制执行,那么使用接口仍然是一个好主意吗?
英文:
The Go language has interface types as features, analogous to C-style interfaces. Go's interface types, though, don't seem to be enforced -- they merely define protocol without actually being applied to a type. As they are not enforced, is it still a good idea to use interfaces?
答案1
得分: 12
是的。Go语言不允许构建类型层次结构,因此接口在实现一些多态性时非常重要。考虑一下在sort
包中定义的sort.Interface
:
type Interface interface {
// Len返回集合中的元素数量。
Len() int
// Less返回索引i的元素是否应该在索引j的元素之前排序。
Less(i, j int) bool
// Swap交换索引i和j的元素。
Swap(i, j int)
}
sort
包中包含一个期望任何实现了该接口的对象的函数sort(data Interface)
。如果没有接口,这种形式的多态性在Go语言中是不可能的。你不需要显式地注明你的类型实现了这个接口,这一点是无关紧要的。
Go语言的一个很酷的特性是,你甚至可以在原始类型上实现这个接口,只要该类型在同一个包中定义。因此,下面的代码定义了一个可排序的整数数组:
type Sequence []int
// sort.Interface所需的方法。
func (s Sequence) Len() int {
return len(s)
}
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
英文:
Yes. Go doesn't allow you to build type-hierarchies, so interfaces are very important to allow some polymorphism. Consider the sort.Interface
defined in the package sort
:
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less returns whether the element with index i should sort
// before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
The sort
package contains a function sort(data Interface)
that expects any object that implements this interface. Without interfaces, such form of polymorphism would not be possible in go. The fact that you don't have to explicitly annotate that your type implements this interface, is irrelevant.
The cool part about go is that you can even implement this interface on primitive types, as long as the type is defined in the same package. So the following code defines a sortable array of integers:
type Sequence []int
// Methods required by sort.Interface.
func (s Sequence) Len() int {
return len(s)
}
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
答案2
得分: 3
是的,使用接口仍然是一个很好的想法。Go强调接口。请参见https://talks.golang.org/2014/go4gophers.slide#5。在扩展@ElianEbbing的出色答案的基础上,任何类型的方法和临时接口都可以实现轻量级的面向对象编程风格。请参见https://talks.golang.org/2012/goforc.slide#48。
一个示例可以说明这一点。
package main
import . "fmt"
func main() {
c.Cry() // Meow
Cryer.Eat(c) // Fish
}
var c = cat{"Fish"}
type cat struct{ s string }
type Eater interface{ Eat() }
type Cryer interface {
Cry()
Eater
}
func (c cat) Eat() { Println(c.s) }
func (cat) Cry() { Println("Meow") }
Cryer
接口允许您访问Cry()
和Eat()
方法。请参见https://talks.golang.org/2012/zen.slide#16。您可以在不更改底层代码的情况下添加Cryer
。请参见https://stackoverflow.com/a/11753508/12817546。如果我们删除它,代码仍然可以正常工作。您仍然可以直接访问方法,如c.Cry()
所示。这是因为Go允许您做两件事。
首先,您可以隐式地实现一个接口。接口只是一组方法。请参见https://talks.golang.org/2013/go4python.slide#33。只要一个非接口值实现了接口的方法,接口变量就可以存储任何非接口值。请参见https://blog.golang.org/laws-of-reflection。
任何类型的值只要实现了接口的所有方法,就可以赋值给该接口的变量。请参见https://talks.golang.org/2014/taste.slide#20。
c
是cat
的变量。cat
实现了Eat()
和Cry()
方法。Eater
显式实现了Eat()
。Cryer
显式实现了Cry()
和Eat()
。因此,cat
隐式实现了Eater
和Cryer
接口。请参见https://talks.golang.org/2012/goforc.slide#40。因此,c
作为cat
的变量可以是Eater
和Cryer
的变量。请参见https://golang.org/doc/effective_go.html#blank_implements。
其次,结构体和接口类型可以嵌入在其他结构体和接口类型中。请参见https://golang.org/doc/effective_go.html#embedding。Cryer.Eat(c)
调用Eat()
,因为它嵌入了Eater
。因此,Go中的接口是解耦程序组件的主要手段。请参见https://www.reddit.com/r/golang/comments/6rwq2g。以及为什么接口可以在包、客户端或服务器之间提供良好的API。请参见https://stackoverflow.com/a/39100038/12817546。
如果您看不到好处,那就不要添加接口。请参见https://stackoverflow.com/a/39100038/12817546中的评论。没有明确的层次结构,因此也没有设计的必要!在需要时添加接口。首先定义您的数据类型,然后根据需要逐步构建您的1-3个方法的接口。引用已编辑以匹配示例。
英文:
Yes, it is still an excellent idea to use interfaces. Go emphases interfaces. See https://talks.golang.org/2014/go4gophers.slide#5. Extending @ElianEbbing outstanding answer, methods on any types and ad hoc interfaces make for a light-weight OO programming style. See https://talks.golang.org/2012/goforc.slide#48.
An example might illustrate this.
package main
import . "fmt"
func main() {
c.Cry() // Meow
Cryer.Eat(c) // Fish
}
var c = cat{"Fish"}
type cat struct{ s string }
type Eater interface{ Eat() }
type Cryer interface {
Cry()
Eater
}
func (c cat) Eat() { Println(c.s) }
func (cat) Cry() { Println("Meow") }
The Cryer
interface lets you access both the Cry()
and Eat()
methods. See https://talks.golang.org/2012/zen.slide#16. You can add Cryer
to the underlying code without its alteration. See https://stackoverflow.com/a/11753508/12817546. And the code works fine if we remove it. You can still access a method directly as shown by c.Cry()
. This is because Go lets you do two things.
First, you can implement an interface implicitly. An interface is simply a set of methods. See https://talks.golang.org/2013/go4python.slide#33. An interface variable can store any non-interface value as long as it implements the interface's methods. See https://blog.golang.org/laws-of-reflection.
Values of any type that implement all methods of an interface can be assigned to a variable of that interface. See https://talks.golang.org/2014/taste.slide#20.
c
is a variable of cat
. cat
implements the Eat()
and Cry()
methods. Eater
explicitly implements Eat()
. Cryer
explicitly implements Cry()
and Eat()
. Therefore cat
implicitly implements the Eater
and Cryer
interface. See https://talks.golang.org/2012/goforc.slide#40. Thus c
a variable of cat
can be a variable of Eater
and Cryer
. See https://golang.org/doc/effective_go.html#blank_implements.
Second, struct and interface types can be embedded in other struct and interface types. See https://golang.org/doc/effective_go.html#embedding. Cryer.Eat(c)
calls Eat()
since it embeds Eater
. Interfaces in Go are thus the primary means for decoupling components of our programs. See https://www.reddit.com/r/golang/comments/6rwq2g. And why interfaces can provide a nice API between packages, clients or servers. See https://stackoverflow.com/a/39100038/12817546.
If you don’t see a benefit, then don’t add an interface. See the comment in https://stackoverflow.com/a/39100038/12817546. There is no explicit hierarchy and thus no need to design one! See https://talks.golang.org/2012/goforc.slide#46. Add an interface when you need it. Define your data types first and build your 1-3 method(s) interface
as you go along. See https://stackoverflow.com/a/11753508/12817546. Quotes edited to match example.
答案3
得分: 0
我希望了解类型检查的含义,包括静态类型检查和运行时类型检查,但我不知道"强制实现接口"是什么意思。
英文:
I hope to know what a type check, both statical and at run time is, but I have no idea what is to "enforce an interface".
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论