我是一名 Java 程序员,正在学习 Go 编程。到目前为止,我真的很喜欢这种语言。比 Java 多很多。
但有一件事我有点困惑。Java 有接口,因为类只能从一个类继承。既然 Go 允许多重继承,那么接口的意义何在?
我是一名 Java 程序员,正在学习 Go 编程。到目前为止,我真的很喜欢这种语言。比 Java 多很多。
但有一件事我有点困惑。Java 有接口,因为类只能从一个类继承。既然 Go 允许多重继承,那么接口的意义何在?
多态性
接口使函数能够具有“占位符”参数,该参数可以将不同的结构作为参数。例如,如果结构体 Man、Woman、Child 实现接口 Human,则带有参数 Human 的方法可以将结构体 Man、Woman、Child 中的任何一个作为参数。因此,接口参数可以“变形”为作为参数传递的任何结构,只要它实现了接口中定义的所有函数。
这很重要,因为接口是在 Go 中实现多态性的唯一方法,因为它没有继承。因此,如果 Man “扩展” Human(通过将其作为匿名字段),则任何使用 Human 作为参数的方法都无法将 Man 作为参数。
我的困惑源于这样一个事实,即继承也是在 Java 中实现多态性的一种方式,我认为这里也是如此。我站纠正!
Go 中的接口与 Java 中的接口非常不同。
在 Java 中,一个类必须正式同意实现一个接口:
public class Foo implements iFoo
在 Go 中,用户类型通过简单地实现接口。
然后,函数或属性可以定义预期的内容:
func DoSomething(r io.Reader) {
buf := make([]byte, 128)
n, err := r.Read(buf)
...
}
DoSomething
函数可以传递任何实现Read
在接口中找到的函数的东西io.Reader
,而无需知道或关心接口。调用者有责任确保它传入了实现接口的东西。这是在编译时检查的。
我们可以更进一步。我们可以定义自己的接口:
type MyInterface interface {
io.Reader // Include Reader interface
Seek(int) error // Include my own function
}
func DoSomething(r MyInterface) {
buf := make([]byte, 128)
n, err := r.Read(buf)
...
}
Go 的不同之处还在于它没有类或对象类型。任何用户声明的类型,无论是基于整数、字符串、结构、数组、切片、通道等,都可以附加方法。
Go 也没有你通常习惯的典型的类继承,但它确实有一些非常接近的东西。
重新声明的类型:
type Num int
func (n Num) Print() {
print(n)
}
type Number Num
func (n Number) Print() {
Num(n).Print()
}
匿名字段:
type Foo struct {
sync.Mutex
}
func main() {
f := Foo{}
f.Lock()
// ...
f.Unlock()
}
如果超类型 X 是一个接口,那么维护代码的人会立即知道它没有方法实现。如果超类型 Y 是一个抽象类,那么维护代码的人必须检查是否有方法实现。所以这是一个文档/维护/可读性的事情。
类可以从多个类文件继承和实现。
除非我误解了:
public class MyClass extends MySuperClass implements MyInterface, MySecondInterface
接口的重点是允许一个完全抽象的类。如此抽象以至于没有定义一个方法。当我需要创建几个具有相同基本结构的抽象类时,我会使用接口。然后,我将能够创建扩展抽象类的类的实例,而抽象类又将实现接口。
这是通过接口java.util.Collection
和一些类似java.util.ArrayList
并java.util.Stack
实现该接口的类来完成的。通过这种方式,您可以将各种列表项存储在一个集合中。这就是为什么ArrayList
有一个方法addAll(Collection<? extends E> c)
。
您可以说这就像与更简单的对象向后兼容。