Go语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性。
结构体成员是由一系列的成员变量构成,这些成员变量也被称为字段。字段有一下特性:
- 拥有自己的类型和值
- 字段名必须唯一
- 字段的类型也可以是结构体,甚至是字段所在结构体的类型。
注意:Go语言中没有类的概念,也不支持类的继承等面向对象的概念。
定义格式如下:
type 类型名 struct{
字段1 字段1 类型
...
//例如
type Point struct{
X int
Y int
}
}
实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,各个实例间的内存是完全独立的。
1.基本实例化的形式
//ins为结构体实例名称,T为结构体类型
var ins T
2.创建指针类型的结构体
ins := new(T)
注意:
- new实例化的结构体返回的是指针,但是和普通的实例化的用法一致。
- 这点和C++不同,C++中指针访问成员必须使用"->"操作符。
- Go之所以不同是因为使用了语法糖(Syntactic sugar)
3.取结构体的地址实例化
取地址格式如下:
ins :=&T{}
//示例
type Command struct{
Name string
Var *int
Comment string
}
var version int =1
cmd :=&Command{}
cmd.Name="version"
cmd.Var=&version
cmd.Comment="show version"
1**.键值对初始化;**
type Command struct{
Name string
Var *int
Comment string
}
cmd :=&Command{
Name:"version",
Var : 1,
Comment: "show version",
}
2.使用多个值的列表初始化结构体
type Command struct{
Name string
Var *int
Comment string
}
cmd :=Command{
"version"
1
"show version"
}
3.初始化匿名结构体
//定义部分
msg :=struct{
ind int
data string
}{ //值初始化部分
"1024"
"hello"
}
Go语言没有构造函数的功能。结构体的初始化过程可以使用函数封装实现。
使用结构体描述猫的特性,根据猫的颜色和名字就可以有不同的种类的猫。就可以创造不同的实例
type Cat struct{
Color string
Name string
}
func NewCatByName(name string) *Cat{
return &Cat{
Name:name,
}
}
func NewCatByName(color string) *Cat{
return &Cat{
Color:color
}
}
实现类似于父子类关系的派生:
黑猫是猫,但是猫是一种猫的泛称。在C++语言中,猫就是基类,黑猫就是猫的派生类。那么Go语言中怎样实现?
type Cat struct{
Color string
Name string
}
type BlackCat struct{
Cat //嵌入Cat,类似于派生
}
//构造基类
func NewCat(name string)* Cat{
return &Cat{
Name:name
}
//模拟构造子类
func NewBlackCat(color string)* BlackCat{
cat :=&BlackCat()
cat,color=color
return cat
}
}
Go语言中的方法是一种作用于特定类型变量的函数。这种特定类型变量叫做接收器。
在go语言中接收器的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。
1.Go语言的结构体方法
type Bag struct{
items []int
}
func (b *Bag) Insert(itemid int){
b.items =append(b.items,itemid)
}
func main(){
b:=new(Bag)
b.Insert(1001)
}
格式如下:
func (接收器变量 接收器类型) 方法名(参数列表) (返回参数){
}
类型:指针接收器、非指针接收器。两种接收器会被用于不同性能和功能要求的代码中。
1.指针类型的接收器。
指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的this或者self.
由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。
我的理解是其用法很像是将类换成了结构体,然后接口就是面向对象中的方法。
示例代码:
package main
import (
"fmt"
)
//定义属性结构
type Property struct {
value int //属性值
}
//设置属性值
func (p *Property) SetValue(v int) {
//修改p的成员变量
p.value = v
}
//取属性值
func (p *Property) Value() int {
return p.value
}
func main() {
//实例化属性
p := new(Property)
//设置值
p.SetValue(100)
//打印值
fmt.Println(p.Value())
}
输出:100
2.理解非指针类型的接收器
当方法作用于非指针接收器时,Go语言会在代码运行时将接收器的值复制一份。在非指针接收器的方法中可以获取接收器的成员值,但修改后无效。
示例代码:
package main
import (
"fmt"
)
//定义点结构
type Point struct {
X int
Y int
}
//非指针接收器的加方法
func (p Point) Add(other Point) Point {
//成员值与参数相加后返回新的结构
return Point{p.X + other.X, p.Y + other.Y}
}
func main() {
//初始化点
p1 := Point{1, 1}
p2 := Point{2, 2}
//与另外一个点相加
result := p1.Add(p2)
//输出结果
fmt.Println(result)
}
输出:{3,3}
Go语言可以对任何类型添加方法,给一种类型添加方法就像给结构体添加方法一样,因为结构体也是一种类型。
1.为基本类型添加方法:
package main
import (
"fmt"
)
//将int定义为MyInt类型
type MyInt int
//为MyInt添加IsZero方法
func (m MyInt) IsZero() bool {
return m == 0
}
//为MyInt添加Add()方法
func (m MyInt) Add(other int) int {
return other + int(m)
}
func main() {
var b MyInt
fmt.Println(b.IsZero())
b = 1
fmt.Println(b.Add(2))
}
2.http包中的类型方法
Go语言提供的http包里也大量使用了类型方法。
下面创建一个HTTP请求,并且设定HTTP头。
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
func main() {
client := &http.Client{}
//创建一个HTTP请求
req, err := http.NewRequest("POST", "http://www.163.com", strings.NewReader("key=value"))
//发现错误就打印退出
if err != nil {
fmt.Println(err)
os.Exit(1)
return
}
//为标头添加信息
req.Header.Add("User-Agent", "myClient")
//开始请求
resp, err := client.Do(req)
//错误处理
if err != nil {
fmt.Println(err)
os.Exit(1)
return
}
//读取服务器返回的内容
data, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(data))
defer resp.Body.Close()
}
输出:
在这个例子中,req.Header就是典型的自定义类型。
定义类型如下:
type Header map[string] []string
func (h Header) Add (key,value string){
textproto.MIMEHeader(h).Add(key,value)
}
结构体允许其成员在声明时没有字段名而只有类型,这种形式的字段被称为类型内嵌或匿名字段。
1.类型内嵌:同种类型的匿名字段只能有一个。
写法如下:
type Data struct{
int
float32
bool
}
ins := &Data{
int: 10,
float32: 3.14,
bool: true,
}
2.结构体内嵌
使用结构体内嵌处理计算机图形学的颜色和透明度表示:
package main
import (
"fmt"
)
type BasicColor struct {
R, G, B float32
}
type Color struct {
BasicColor
Alpha float32
}
func main() {
var c Color
c.R = 1
c.G = 1
c.B = 1
c.Alpha = 1
fmt.Printf("%+v", c)
}
输出:
1.内嵌的结构体可以直接访问其成员变量
2.内嵌结构体的字段名是它的类型名
在面向对象思想中,实现对象关系需要使用"继承特性"。如,人类不能飞行,鸟可以飞行。人类和鸟类都可以继承自行走类,但只有鸟类继承自飞行类。
Go语言的结构体内嵌实现对象特性组合。
也可以理解为Go语言的面向对象中的继承实现方式
package main
import (
"fmt"
)
//可飞行的
type Flying struct {
}
func (f *Flying) Fly() {
fmt.Println("can fly")
}
//可行走的
type Walkable struct {
}
func (f *Walkable) Walk() {
fmt.Println("can walk")
}
//人类
type Human struct {
Walkable
}
//鸟
type Bird struct {
Walkable
Flying
}
func main() {
//实例化鸟类
b := new(Bird)
fmt.Println("Bird:")
b.Fly()
b.Walk()
fmt.Println("---------------")
//实例化人类
h := new(Human)
fmt.Println("Human:")
h.Walk()
}
package main
import (
"fmt"
)
//车轮
type Wheel struct {
Size int
}
//引擎
type Engine struct {
Power int //功率
Type string //类型
}
//车
type Car struct {
Wheel
Engine
}
func main() {
c := Car{
//初始化轮子
Wheel: Wheel{
Size: 18,
},
Engine: Engine{
Type: "1.4T",
Power: 143,
},
}
fmt.Printf("%+v\n", c)
}
package main
import (
"fmt"
)
//车轮
type Wheel struct {
Size int
}
//车
type Car struct {
Wheel
Engine struct {
Power int //功率
Type string //类型
}
}
func main() {
c := Car{
//初始化轮子
Wheel: Wheel{
Size: 18,
},
Engine: struct {
Power int
Type string
}{
Type: "1.4T",
Power: 143,
},
}
fmt.Printf("%+v\n", c)
}
如果嵌入结构体内部可能拥有相同的成员名,成员重名时编译器会报错。如:
现在有结构体如下:
package main
import (
"fmt"
)
type A struct {
a int
}
type B struct {
a int
}
type C struct {
A
B
}
func main() {
c := &C{}
c.a = 1
fmt.Println(c)
}
编译器会报错。将c.a改成c.A.a=1之后,会输出结果。因为如果像前者那样使用,编译器分不清到底将1赋给C中A的a还是B的a。