第3章 面向对象编程
Go语言没有继承、虚函数、构造析构函数、this指针等。
3.1类型系统
类型系统:
- 基础类型
- 复合类型
- 指向对象的任意类型Any
- 值语义和引用语义
- 面向对象
- 接口
为类型添加方法
go可以为任何类型添加方法(包括内置类型,但不包括指针类型)。
go语言没有隐藏的this指针。也就是方法施加的对象显式传递,不需要非得是指针,也不用非得叫this。
&取地址,*取内容和表示指针。
当需要修改对象时,类方法必须用指针。
func (a *Integer) Add(b Integer){
*a +=b
}
//不修改对象就不用加指针,保护数据但有内存开销。
func (a Integer) Add(b Integer){
c := a+b
fmt.Print(c)
}
值语义和引用语义
go语言大多数类型都是值语义。基本类型,以及数组、结构体、指针都是值语义。
对数组a,b=a是内容的完整复制,如想表达引用需使用指针var b=&a。
以下4个类型是引用类型:
- 数组切片
- map
- channel
- interface
channel和map本质上是一个指针,考虑到复制channel和map并不是常规要求,所以设计为引用类型而非统一的值类型。
初始化
go没有构造函数。对象创建由全局的创建函数执行(固定的NewXXX命名):
//创建Rect对象,注意结构体是值语义,返回值需取址转成指针。
func NewRect(x,y,width,height float64) *Rect{
return &Rect{x,y,width,height}
}
匿名组合
go调用类成员的类方法,相当于调用父类的方法。没有继承重写的概念。
type Base struct{}
type Foo struct{
Base
}
func (foo *Foo) Bar(){
foo.Base.Bar()
}//相当于继承
指针方式从一个类派生:(匿名组合)
type Foo struct{
*Base
}
var foo Foo
foo.Bar()
//匿名使用类成员的方法,可以当作继承用。
//构造时需要外部提供一个Base类实例的指针。
需要注意,无论匿名组合还是非匿名组合,调用的接收方并没有改变。被调用的Bar()方法不能访问Foo结构体中除Base结构体以外的其它方法。(缺点和问题)
对于命名冲突问题:
type X struct{
Name string
}
type Y struct{
X
Name string
}
//Y对象只会访问到外层的Name变量,X.Name变量被隐藏起来了。
type Logger struct{}
type Y stuct{
*Logger
Name string
*log.Logger
}
//重名的成员变量会导致编译错误,但如果不都使用,则编译器会忽略这个错误。
可见性
大写包外可见。
go的访问性是包一级而不是类型一级的,类方法也可以被同包的其它类使用。
接口
侵入式接口:实现类需要声明自己实现了某个接口。
go语言是非侵入式接口:只要实现了接口要求的所有函数,就是实现了接口。因百不必关心以下问题:
- 两个类实现了相同的接口,放在哪个包中才好。
- 提供哪些接口。
意义:
- go中,类的继承树并无意义,只需要知道类实现了哪些方法和每个方法的含义。
- 实现与只需要关心:需要提供哪些方法。不必关注接口拆多细。
- 不必为了实现接口而导入一个包。引入外部包意味着耦合。
接口赋值(重点)
定义一个接口和实现类:
type Integer int
func (a Integer) Less(b Integer) bool{
return a<b
}
func (a *Integer) Add(b Integer) {
*a += b
}
type LessAdder interface {
Less(b Integer) bool
Add(b Integer)
}
func main() {//无返回值不用写void
var a Integer = 1
var b LessAdder = &a
var c LessAdder = a//编译器报错,由于带有引用语义的方法
}
go语言根据
func (a Integer) Less(b Integer) bool
自动生成
func (a *Integer) Less(b Integer) bool{
return (*a).Less(b)
}
这样*Integer类型即存在Less()方法,也存在Add()方法,满足LessAdder()接口。而另一方面,根据
func (a *Integer) Add(b Integer)
却无法生成
func (a Integer) Add(b Integer){
(&a).Add(b)
}
生成函数不能改变外部参数,与用户预期不符。
对象赋值给接口:因而含有语义方法的接口,只能引用赋值。只含值语义方法的接口随意。
接口之间赋值:并不要求两个接口等价,只要包含被赋值接口的所有声明方法即可。
接口查询
由于if语句带有赋值,接口查询的代码要优雅得多。
var a X = 1
if b,ok := a.(Integer); ok{}//注意是接口查询,a只能是接口实例,而不能是结构体的实例。
用switch询问类型:(类型查询)
var v1 interface{} = ...
switch v := v1.(type){
case int:
case string:
}
是的,利用反射也可以进行类型查询,详情可参阅reflect.TypeOf()方法的相关文档。
接口组合
go语言的接口即可以写方法,也可以写其它接口。
type ReadWriter interface{
Reader //Reader接口
Writer //Writer接口
}
Any类型
空接口可以接受任意对象的实例:
var v1 interface{} = 1
var v2 interface{} = "abc"
var v3 interface{} = &v2
函数可以接受任意对象实例时,也可以声明为interface{}
func Printf(fmt string,args ...interface{})
func Println(args ...interface{})
网友评论