go语言中的大多数类型都是值予以,并且都可以包含对应的操作方法,在需要的时候你可以给任意类型增加新方法。二在实现某个接口时,无需从该接口集成,只需要实现该接口要求的所有方法即可。任何类型都可以被any类型引用。any类型是空接口 interface{}
在Go语言中,你可以给任意类型(包括内置类型,但不包括指针类型)添加相应的方法 .如下
-
-
package main
-
import (
-
"fmt"
-
)
-
func main() {
-
person.Go2School()
-
}
-
func (ps Person) Go2School() {
-
fmt.Println("go to school")
-
}
-
Go语言中的大多数类型都基于值语义,包括:
-
基本类型,如byte、int、bool、float32、float64和string等;
-
复合类型,如数组(array)、结构体(struct)和指针(pointer)等。
值语义和引用语义
-
a := 10
-
b := a
-
b = b + 1
-
fmt.Println(b)
-
fmt.Println(a)
-
输出:11,10
-
c := 10
-
d := &c
-
*d += 1
-
fmt.Println(c)
-
fmt.Println(*d)
-
输出:11,11
Go语言中有4个类型比较特别,看起来像引用类型
数组切片:指向数组(array)的一个区间。
map:极其常见的数据结构,提供键值查询能力。
channel:执行体(goroutine)间的通信设施。
接口(interface):对一组满足某个契约的类型的抽象
结构体
-
-
定义:
-
type Rect struct {
-
x, y float64
-
width, height float64
-
}
-
初始化
-
rect1 := new(Rect)
-
rect2 := &Rect{}
-
rect3 := &Rect{0, 0, 100, 200}
-
rect4 := &Rect{width: 100, height: 200}
构造函数
在Go语言中,未进行显式初始化的变量都会被初始化为该类型的零值,例如bool类型的零值为false,int类型的零值为0,string类型的零值为空字符串。
在Go语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以NewXXX来命名,表示“构造函数”:
-
-
package main
-
//person类
-
type Person struct {
-
Name string
-
Age int
-
Sex string
-
}
-
//person构造函数
-
func NewPerson(name string, sex string, age int) *Person {
-
return &Person{Name: name, Sex: sex, Age: age}
-
}
-
//person类ResetName1方法 (传值)
-
func (ps Person) ResetName1(name string) {
-
ps.Name = name
-
}
-
//person类ResetName1方法 (传址)
-
func (ps *Person) ResetName2(name string) {
-
ps.Name = name
-
}
-
//student类
-
type Student struct {
-
Class string
-
Grade string
-
*Person
-
}
-
//student构造函数
-
func NewStudent(name string, sex string, age int, class string, grade string) *Student {
-
return &Student{Person: NewPerson(name, sex, age), Class: class, Grade: grade}
-
}
匿名组合:类的继承是使用了匿名组合的方式
-
-
package main
-
type Person struct {
-
Name string
-
Age int
-
Sex string
-
}
-
//构造函数
-
func NewPerson(name string, sex string, age int) *Person {
-
return &Person{Name: name, Sex: sex, Age: age}
-
}
-
func (ps Person) ResetName1(name string) {
-
ps.Name = name
-
}
-
func (ps *Person) ResetName2(name string) {
-
ps.Name = name
-
}
-
//继承自Person
-
type Student struct {
-
Class string
-
Grade string
-
Person //或者*Person
-
}
-
这样Student就继承自了 Person类
可见性
-
要使某个符号对其他包(package)可见(即可以访问),需要将该符号定义为以大写字母开头
-
-
type Rect struct {
-
X, Y float64
-
Width, Height float64
-
}
-
这样,Rect类型的成员变量就全部被导出了,可以被所有其他引用了Rect所在包的代码访问到。
-
func (r *Rect) area() float64 {
-
return r.Width * r.Height
-
}
-
这样,Rect的area()方法只能在该类型所在的包内使用。
-
Go语言中符号的可访问性是包一级的而不是类型一级的
-
接口
-
入侵接口
c#和Java中的接口时入侵是接口
-
非入侵接口
go的接口是非入侵式接口
在Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口
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()等方法。设想我们有如下接口:
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
Seek(off int64, whence int) (pos int64, err error)
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
Close() error
尽管File类并没有从这些接口继承,甚至可以不知道这些接口的存在,但是File类实现了这些接口,可以进行赋值:
var file1 IFile = new(File)
var file2 IReader = new(File)
var file3 IWriter = new(File)
var file4 ICloser = new(File)
Go语言的非侵入式接口,看似只是做了很小的文法调整,实则影响深远。
其一,Go语言的标准库,再也不需要绘制类库的继承树图。你一定见过不少C++、Java、C#类库的继承树图。在Go中,类的继承树并无意义,你只需要知道这个类实现了哪些方法,每个方法是啥含义就足够了。
其二,实现类的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。接口由使用方按需定义,而不用事前规划。
其三,不用为了实现一个接口而导入一个包,因为多引用一个外部的包,就意味着更多的耦合。接口由使用方按自身需求来定义,使用方无需关心是否有其他模块定义过类似的接口
接口赋值
接口赋值在Go语言中分为如下两种情况:
-
将对象实例赋值给接口;
-
将接口实例赋值给接口;
-
//对象赋值给接口
-
-
var interfaces IStudent = NewStudent("Jessica", "male", 18, "class1", "grade1")
-
interfaces.Go2School()
在Go语言中,只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等同的,可以相互赋值
-
-
package one
-
type ReadWriter interface {
-
Read(buf []byte) (n int, err error)
-
Write(buf []byte) (n int, err error)
-
}
-
package two
-
type IStream interface {
-
Write(buf []byte) (n int, err error)
-
Read(buf []byte) (n int, err error)
-
}
这里我们定义了两个接口,一个叫one.ReadWriter,一个叫two.Istream,两者都定义了Read()、Write()方法,只是定义次序相反。one.ReadWriter先定义了Read()再定义了Write(),而two.IStream反之。
在Go语言中,这两个接口实际上并无区别,因为:
-
任何实现了one.ReadWriter接口的类,均实现了two.IStream;
-
任何one.ReadWriter接口对象可赋值给two.IStream,反之亦然;
-
在任何地方使用one.ReadWriter接口与使用two.IStream并无差异。
以下这些代码可编译通过:
-
-
var file1 two.IStream = new(File)
-
var file2 one.ReadWriter = file1
-
var file3 two.IStream = file2
接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A。例如,假设我们有Writer接口:
type Writer interface {
Write(buf []byte) (n int, err error)
}
就可以将上面的one.ReadWriter和two.IStream接口的实例赋值给Writer接口:
var file1 two.IStream = new(File)
var file4 Writer = file1
接口查询
switch进行接口查询
-
-
// OOPTest project main.go
-
package main
-
type IStudent interface {
-
Go2School()
-
}
-
type IPerson interface {
-
Speak(word string)
-
Eat(food string)
-
}
-
func main() {
-
//std := NewStudent("Jessica", "male", 18, "class1", "grade1")
-
//psn := NewPerson("James", "female", 20)
-
//语句switch中的value必须是接口类型,变量str的类型为转换后的类型。/
-
var IStd interface{} = NewStudent("Jessica", "male", 18, "class1", "grade1")
-
switch per := IStd.(type) {
-
case IStudent:
-
per.Go2School()
-
case IPerson:
-
per.Eat("pig")
-
}
-
}
类型断言
-
-
// OOPTest project main.go
-
package main
-
type IStudent interface {
-
|
请发表评论