刚开始学go的时候,第一遍文档看完,没有搞懂结构体和接口的意思,现在大概明白了,记录一下。
函数和方法
在了解结构体和接口之前
在我写JavaScript的时候,大部分不区分这两个叫法,但是在Java中,定义在类中的函数,习惯称之为”方法”。
我在网上找到这么个说法:
- 函数是独立的功能,与对象无关,需要显示的传递数据
- 方法与对象和类相关,依赖对象而调用,可以直接处理对象上的数据,也就是隐式传递数据
- c语言中只有函数
emm… 和我理解的差不多。。
在golang中可以这么理解:
- 函数属于一个包,分为公有函数(首字母大写,其他包可以用),私有函数(首字母小写,只能当前包内部使用)。
- 方法属于一个结构体。
结构体
结构体的声明和使用:
1 2 3 4 5 6 7 8 9
| type Person struct{ age int name string }
p := &Person{age:22,name:"ruomu"} fmt.Printf("age= %d, name= %s",p.age,p.name)
|
结构体的嵌套:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| type Address struct{ country string city string }
type Person struct{ age int name string add Address }
p := &Person{age:22,name:"ruomu",add:Address{country:"china",city:"beijing"}} p.add.city
|
结构体的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| type Person struct{ age int name string }
func (p *Person) Hello(){ fmt.Println("hello ",p.name) }
p := Person{age:22,name:"ruomu"} p.Hello()
|
我们仔细一点会发现上面的代码中,接收者p *Person是一个指针类型,但是我们调用者p := Person{age:22,name:"ruomu"} 却是一个值类型。
事实上:
- 方法的接收者既可以是一个值类型,也可以是一个指针类型。
- 如果使用一个值类型变量调用指针类型接收者的方法,Go 语言编译器会自动帮我们取指针调用,以满足指针接收者的要求。
- 同样的原理,如果使用一个指针类型变量调用值类型接收者的方法,Go 语言编译器会自动帮我们解引用调用,以满足值类型接收者的要求。
所以,接收者定义为值类型还是指针类型,取决你想不想修改原始对象的值,不用考虑调用方是不是指针类型。
**方法可以赋值给一个变量,调用时第一个参数必须是该方法的接收者: **
1 2 3 4 5 6 7 8 9 10 11 12 13
| type Person struct{ age int name string }
func (p Person) Hello(){ fmt.Println("hello ",p.name) }
h := Person.Hello h(p)
|
这里Hello方法的接收者写成指针类型就会报错,试了几种写法都不行。。。
接口
以上面的Person结构体为例
接口的定义和实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| type WalkRun interface { Walk() Run() }
type Person struct { age int name string }
func (p *Person)Walk(){ fmt.Print("walk",p.name) }
func(p *Person)Run(){ fmt.Printf("%s run",p.name) }
func main() { p := &Person{name:"zm",age:1} WalkRun.Run(p) WalkRun.Walk(p) }
|
和java一样,如果一个接口有多个方法,那么需要实现接口的每个方法才算是实现了这个接口。(抽象类除外)
这里有一点需要注意,和结构体的方法不同,实现了接口的方法:
- 接收者是指针类型时,调用必须传入指针类型。
- 接收者是值类型时,调用者随意。(指针类型会被转为值类型,和前文描述的行为相同)
继承和组合
go语言中,没有继承的概念,所以go语言利用的是组合来达到代码复用的目的。
以go语言的io标准包的接口为例:
1 2 3 4 5 6 7 8 9 10 11
| type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) }
type ReadWriter interface { Reader Writer }
|
当然结构体也可以组合:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| type Address struct{ country string city string }
type Person struct{ age int name string Address }
p := &Person{age:22,name:"ruomu",Address:Address{country:"china",city:"beijing"}} p.city
|
写在最后:
接口组合并不是继承,因为它虽然可以复用”被继承”的接口的方法,但是却无法重写该方法。
感觉就是个语法糖,由于我oop接触的不多,反而不受java那套影响,更容易接受一些。。
参考链接: