go 语言仅支持封装,不支持继承和多态
go 语言没有class 只有struct
结构体
声明和创建
type treeNode struct {
value int
left, right *treeNode
}
func main() {
var root treeNode
root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)
nodes := []treeNode {
{value: 3},
{},
{6, nil,&root },
}
// [{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> 0xc4200a2020}]
fmt.Println(nodes)
}
go 本身不提供构造函数,虽然提供了那么多构造的方式,但是还是会有使用到构造函数的需求,这时我们可以自己写一个工厂函数
需要注意的是,这里返回了局部变量的地址,但是我们不需要关心这个变量是被分配到堆还是栈上,go内部的垃圾回收机制会帮我们解决这个问题
func createNode(value int) *treeNode {
return &treeNode{value: value}
}
如果需要对结构体增加扩展方法,我们可以采用以下的方式
func (node treeNode) print() {
fmt.Println(node.value)
}
func main() {
var root treeNode
root = treeNode{value: 3}
// 3
root.print()
}
封装
go 语言中的封装使用名字来区分,作用域是包
- 首字母大写代表 public
- 首字母小写代表 private
一个文件的目录下只能有一个包
duck typing
- 走路像鸭子、叫起来像鸭子、长的像鸭子,那么就是鸭子
- 描述食物的外部行为而非内部结构
- 严格说go属于结构化类型系统,类似duck typing
python中的 duck typing
运行时才知道传入的retriever有没有get方法,且需要注释来说明接口
def download(retrivever):
return retriever.get("www.baidu.com")
c++中的 duck typing
编译时才知道传入的retriever有没有get方法,且需要注释来说明接口
template <class R>
string download(const R& retriever) {
return retriever.get("www.baidu.com")
}
java中的类似代码
传入的参数必须实现IRetriever接口,但不是duck typing
<R extends IRetriever>
String download(R r) {
return r.get("www.baidu.com")
}
C#中类似代码
传入的参数必须实现IRetriever接口,但也不是duck typing
string download(R r) where r : IRetriever {
return r.get("www.baidu.com")
}
go语言的 duck typing
具体实现见接口
- 同时可以满足Readable、Appendable
- 同时具有python、c++的duck typing的灵活性
- 又具有java、c#的类型检查
接口
在下面的例子中,我们可以看出go 的interface 的实现与其他语言的区别,并遵守了duck typing
package test
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
package main
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("http://www.baidu.com")
}
func main() {
var r Retriever
r = test.Retriever{"test"}
// test
fmt.Println(download(r))
}
接口的类型
试着输出这个接口的类型,看看接口到底是什么
func main() {
var r Retriever
r = test.Retriever{"test"}
// test.Retriever {test}
fmt.Printf("%T %v\n", r, r)
r = &reall.Retriever{"Mozilla/5.0", time.Minute}
// *reall.Retriever {Mozilla/5.0 1m0s}
fmt.Printf("%T %v\n", r, r)
}
15284544401782.jpg
- 接口变量自带指针
- 接口变量同样采用值传递,几乎不需要使用接口的指针
- 指针接受者实现只能以指针方式使用,值接受者都可以
查看接口变量
在查看接口变量时,我们可以使用Type Assertion、Type Switch
PS:在go语言中,interface{} 表示任何类型
Type Switch
func inspect(r Retriever) {
switch v := r.(type) {
case test.Retriever:
fmt.Println(v.Contents)
case *reall.Retriever:
fmt.Println(v.UserAgent)
}
}
Type Assertion
if testRetriever, ok := r.(test.Retriever); ok {
fmt.Println(testRetriever.Contents)
} else {
fmt.Println("this is not test retriever")
}
组合接口
我们可以通过以下方式对接口进行组合
type Retriever interface {
Get(url string) string
}
type Poster interface {
Post(url string, form map[string]string) string
}
type RetrieverPoster interface {
Retriever
Poster
}
func session(s RetrieverPoster) string {
s.Post("http://www.baidu.com", map[string]string {"name":"rp"})
return s.Get("http://www.baidu.com")
}
网友评论