go语言编程 要点总结(三)面向对象编程

类型系统

可以给任意类型(包括内置类型,但不包括指针类型)添加相应的方法

type Integer int

func (a Integer) Less(b Integer) boo {

}

只有在需要修改对象的时候,才必须用指针

func (a *Integer) Add(b Integer) {

*a += b

}

go语言中类型都是基于值传递的,要像修改变量内容,职能传递指针

go语言中数组是值传递,要想修改内容,使用指针

var a = [3]int{1, 2, 3}

var b = &a

b[1] ++

go语言中有四个类型,看起来比较像引用类型,实际上是结构内部记录有指针,

数组切片,map,channel 和接口

结构体

type Rect struct {

x, y float64

width, height float64

}

结构体方法

func (r *Recv) Area() float {

return r.width *r.height

}

结构体初始化

rect1 := new(Rect)

rect2 := &Recv{}

rect3 := &Rect{0,0,100,200}

rect4 := &Rect(width: 100, height:200)

在go中未进行显示初始化的变量都被初始化为该类型零值,bool是false, int是0,string类型则是空字符串

在go中没有构造函数,对象创建通常由一个全局函数完成,以NewXXX来命名


匿名组合

go中通过组合的方式来实现其他语言中的继承

type Base struct {

Name string

}

func (base *Base) Foo() {}

func (base *Base) Bar(){}

type Foo struct {

Base

}

func (foo *Foo) Bar(){

foo.Base.Bar()

}

没有改写基类方法时,相应的方法就被继承

还可以通过指针的方式派生,但是此时需要在外部提供一个Base类实例的指针

不管是匿名还是非匿名,被组合的类型所包含的方法虽然都升级成了外部这个组合类的方法,但其实被组合方法调用时接受者并没有改变,

接口组合时的名字冲突

type X struct {

Name string

}

type Y struct {

X

Name string

}

不会有冲突,所有对Y类型Name成员的访问都只会访问最外层的Name

type Logger struct {

Level int

}

type Y struct {

*Logger

Name string

*log.Logger

}

匿名组合类型相当于以其类型名称(去掉 包名部分)作为成员变量的名字。按此规则,Y中相当于存在两个名为Logger的成员,会有编译错误

匿名字段struct 所拥有的全部字段都被隐式引入当前定义的struct

匿名字段和外部struct拥有同名变量时,优先访问外部变量,但是可以通过制定匿名字段来访问匿名字段中的变量。例如:Bob.Human.phone


method

method是附属在一个给定的类型上的,它的语法和函数声明语法几乎一样,只是在func后面增加了一个receiver,也就是method所已从的主体

虽然多个类型的method的名字是一样的,但是如果接收者不一样,那么method就不一样

method里面可以访问接收者的字段

调用method通过“.”访问,就像struct里面访问字段一样

method的接收者可以是普通类型,也可以是指针。普通类型作为receiver仅仅是以副本作为操作对象

method可以定义在自定义类型(type typeName typeLiteral)、内置类型、struct等各种类型上

如果一个method的receiver是*T,你可以在一个T类型的实例变量V上面调用这个method,而不需要&V去调用这个method

同理如果receiver是T,也允许在一个*T类型的变量P上调用这个method

method和匿名字段中的其他变量一样,也是可以继承的,如果在外部类型中定义了同名的method,那么也一样先访问外部定义的method


可见性

没有private protected public等关键字

要想某个符号被其他包访问,需要将该符号定义为以大写字母开头

这个限制只是对包,同一个包中的其他对象都可以访问这些符号


接口

interface

是一组抽象方法的集合,它必须由其他非interface类型实现,而不鞥自我实现。

interface变量的内部实际上有一个slice记录了实现这个interface的其他类型。

空interface

不包含任何method,所有类型都实现了空interface

空interfface可以存储任意类型,有点类似void*

如果一个函数返回interface{} ,就可以返回任意的值

interface变量可以持有任意实现该interface类型的对象

实现了error接口的对象,即实现了Error() string的对象,使用fmt输出时,会调用Error()方法,因此不必再定义String()方法

怎么知道一个interface变量里面实际保存的是那个类型的对象呢Common-ok断言和switch测试

common-ok

value, ok = element.(T) value是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型

如果element里面确实存储了T类型的数值,ok就返回true,否则返回false

switch测试

switch value := element.(type) {

case int:

case string:

case Person:

default:

}

强调:element.(type)不能在switch外的任何逻辑里面使用

侵入式接口

主要表现在实现类需要明确声明自己实现某个接口

设计接口的纠结:

提供哪些接口

如果两个类实现了相同接口,应该把接口放在哪个包里

非侵入式接口

一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口,可以进行赋值

影响:

其一,类的继承树并无意义,只需要直到这个类实现了哪些方法

其二,实现类的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。接口由使用方按需定义,不用实现规划。

其三,不用为实现一个接口而引入一个包,多引入一个外部包,就以为着更多的耦合。接口由使用方按需定义,无需关心是否有其他模块定义过类似接口

接口赋值

对象赋值给接口,需要对象实现所有接口的方法

type Integer int

func (a Integer) Less(b Integer) bool {}

func (a *Integer) Add(b Integer) {}

type LessAdder interface {

Less(b Integer) bool

Add(b Integer)

}

var a Integer = 1

var b LessAddr = &a 这个赋值是允许的 编译器可以根据指针类型,生成值类型函数

var b LessAddr = a 这个赋值是错误的

两个接口具有相同的方法列表就可以赋值,另外如果接口A的方法列表是接口B的子集,那么B也可以赋值给A

var file1 = Writer = ...

if file6, ok := file1.(*File); ok {} 查询接口指向的对象是否是*File类型

var v1 interface{} = ...

swititch v := v1.(type) 查询v1对象的类型

反射也可以查询类型

接口组合

type ReadWriter interface {

Reader

Writer

}

等价于

type ReadWriter interface {

Read(p []byte)(n int, err error)

Write(p []byte)(n int, err error)

}

如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式包含interface1的所有method

Any类型

interface{} 可以指向任何对象的Any类型

一个函数可以接受任意类型参数,会将其声明为interface{}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值