参考书:Go语言编程
第三章 面向对象编程
1 Go语言中,你可以给任意类型(包括内置类型,但不包含指针类型)添加相应的方法,如下
type Integer int
func (a Integer) Less(b Integer) bool{ // 给Integer类型定义成员函数
return a < b
}
func main(){
var a Integer = 1
if a.Less(2){
fmt.Println(a," less 2")
}
}
2 Go语言中没有隐藏的this指针,即方法施加的对象为显示传递,也不需要非得是指针,也不用非得叫this
3 数组作为参数传递为值传递,即函数内修改不会影响原数组,数组切片作为参数传递为引用传递,即函数内修改会影响原数组
var a = [3]int{1,2,3}
var b = a // 数组值传递
b[1] ++
fmt.Println(a,b)
var c = &a // 数组引用传递
c[1] ++
fmt.Println(a,*c)
// 输出
[1 2 3] [1 3 3]
[1 3 3] [1 3 3]
4 如果需要修改对象,才必须使用指针,如下
type Integer int
func (a *Integer) Add(b Integer){
*a += b
}
func (a Integer) Add2(b Integer){
a += b
}
func main(){
var a1 Integer = 1
a1.Add(2)
fmt.Println("a = ",a1) // 输出 a = 3,因为Add()传入的是指针
a1.Add2(2)
fmt.Println("a = ",a1) // 输出 a = 3,因为Add2()传入的是值,所以函数修改不影响对象
}
5 Go语言的类型哪些属于值传递
- 基础类型(如byte,int,bool,float32,float64和string等)和复合类型(如数组,结构体和指针等)属于值传递
- 数组切片,map和channel,接口这4种类型一般属于引用类型
注:
1)数组切片本质是指向数组的一个区间,内部是指向数组的指针
2)map本质也是一个字典指针
3)channel和map类型,本质上也是一个指针
4)接口具备引用语义,是因为内部维持了两个指针,示意为:
type interface struct {
data *void
itab *Itab
}
6 结构体,如下
type Rect struct {
x , y float64
width,height float64
}
func (r *Rect) Area() float64{
return r.width * r.height
}
rect1 := new(Rect) // 第一种初始化方式
rect2 := &Rect{} // 第二种初始化方式
rect3 := &Rect{0,0,100,200} // 第三种初始化方式
rect4 := &Rect{width:100,height:100} // 第四种初始化方式
7 匿名组合,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()
}
补充:匿名组合类型相当于以其类型名称(去掉包名部分)作为成员变量名,如下
type Job struct {
Name string
*log.Logger // 等同于 Logger *log.Logger
}
8 Go语言中没有关键字private,public和protected,可访问性是包一级而不是类一级。即小写字母开头的包内可见
9 Go语言采用非侵入式接口
type File struct {
// toDo. . .
}
func (f *File) Read(buf []byte) (n int , err error)
func (f *File) Write(buf []byte) (n int , err error)
func (f *File) Seek(off int64 , whence int) (pos int64 , err error)
func (f *File) Close( ) error
定义了一个File类,并实现有Read( ) , Write( ) , Seek( ) , Close( )等方法。设想我们有如下接口
type IFile interface {
Read(buf []byte) (n int , err error)
Write(buf []byte) (n int , err error)
Seek(off int64 , whence int) (pos int64 , err error)
Close( ) error
}
type IReader interface {
Read(buf []byte) (n int , err error)
}
type IWriter interface {
Write(buf []byte) (n int , err error)
}
type ICloser interface {
Close( ) error
}
var file1 IFile = new(File)
var file2 IReader = new(File)
var file3 IWriter = new(File)
var file4 ICloser = new(File)
10 将对象实例赋值给接口
type Integer int
func (a Integer) Less(b Integer) bool{
return a < b
}
func (a *Integer) Add(b Integer){
*a += b
}
type LessAdder interface {
Add(b Integer)
Less(b Integer) bool
}
type LessAdder2 interface {
Less(b Integer) bool
}
var a Integer = 1
var b LessAdder = a // error,因为LessAdder接口包含Add(),而类型a的Add()函数要改变对象值,需要地址赋值
var c LessAdder = &a // ok
var d LessAdder2 = a // ok,因为LessAdder接口没包含Add(),不要求地址赋值
var e LessAdder2 = &a // ok
11 接口给接口赋值,如果两个接口同样的方法列表可以相互赋值,如果一个接口的方法列表是另一个接口的方法列表的子集。接口可以赋值给子集的那个,不能反了。
type File struct {
// toDo. . .
}
func (f *File) Read(buf []byte) (n int , err error)
func (f *File) Write(buf []byte) (n int , err error)
func (f *File) Seek(off int64 , whence int) (pos int64 , err error)
func (f *File) Close( ) error
type A interface {
Read(buf []byte) (n int , err error)
Write(buf []byte) (n int , err error)
}
type B interface {
Read(buf []byte) (n int , err error)
}
var file1 A = new(File)
var file2 B = file1 // 接口A可以给接口B赋值,接口B是接口A的子集
12 接口查询,如下
if file5 , ok := file1.(IReder) ; ok { // 检查file1接口指向的对象实例是否实现了IReader接口
// toDo
}
13 类型查询,采用 .(type) ,如下
var i int = 12
fmt.Println("i的类型为 ", i.(type)) // i的类型为int
14 Any类型:任何对象实例都满足空接口interface{ },如下
var v1 interface{ } = 1
var v2 interface{ } = "abc"
var v3 interface{ } = &v2
var v4 interface{ } = struct{ X int }{1}