golang其实并不是一个完全意义上的面向对象语言,其实现面向对象的多态主要基于接口和结构体实现。
在golang中结构体与c/c++中的结构体类似,用来定义复杂的数据类型,一般结构体定义如下:
type StructName struct{
fieldName1 type1
fidldName2 type2
...
}
需要注意的是:
- 结构体的包名在同一个包内必须唯一,不能重复
- 结构体的字段必须唯一,不能重复
- 同类型的结构体字段可以放在一行定义
一般结构体实例化可以通过如下方式:
type Order struct {
amount int
price float64
address string
}
func main() {
// example1 这种情况下order的字段都是对应的默认零值
var order Order
order.amount = 10
order.price = 12.5
order.address = "BeiJing,China"
// example2 这种情况下同时初始化了order的属性字段
order = Order{amount:10,price:12.5,address:"BeiJing,China"}
// 或者
// order = Order{10,12.5,"BeiJing,China"}
// example3 通过new方式创建,返回的是该结构体的指针,且属性字段都是默认零值
orderPtr := new(Order)
// 等价于 orderPtr := &Order{}
orderPtr.amount = 10
orderPtr.price = 12.5
orderPtr.address = "BeiJing,China"
}
我们需要注意的一点是,golang中的结构体是值类型
,也就是如果只是单纯的结构体类型而不是指针类型都是值传递,需要通过拷贝复制。
这里说下golang中的拷贝复制:深拷贝
和 浅拷贝
。
所谓的深拷贝就是将原值的内存内容拷贝到一个新的内存地址中去
所谓的浅拷贝,一般是针对指针类型,就是拷贝的不是值的内存地址,而是指针的内容
其实说白了,golang中只有值传递,所谓的深拷贝和浅拷贝,拷贝的都是值,只不过一个是实际类型的数据内容,一个只是指针内容,如果结构体比较复杂,字段属性比较多,
那么直接基于值的深拷贝肯定耗时多,而基于指针的浅拷贝则快速很多(指针一般都是4字节或8字节)
匿名结构体
与函数一样,结构体也有匿名结构体:
order2 := struct {
amount int
name string
}{10,"rice"}
结构体匿名字段
所谓结构体匿名字段,也就是结构体中存在的仅有类型而没有命名的字段,比如:
type Order2 struct {
int
string
price float64
address string
}
需要注意的是,在一个结构体中,同一个类型只能由一个匿名字段
结构体通过匿名字段可以实现类似继承关系:
type Product struct {
price float64
name string
}
type Address struct {
country,province,city,street string
}
type Order struct {
Product
Address
amount int
}
golang中结构体的匿名字段的方法时可以继承的:
type Product struct {
price float64
name string
}
func (p Product) getProductInfo() string {
return fmt.Sprintf("%f %s",p.price,p.name)
}
type Address struct {
country,province,city,street string
}
type Order struct {
Product
Address
amount int
}
type Order2 struct {
int
string
price float64
address string
}
func main() {
product := Product{name:"奥利奥",price:10.5}
address := Address{"中国","北京市","北京市","海淀区"}
var order Order
order.Product=product
order.Address=address
order.amount=10
fmt.Println(order.getProductInfo())
}
另外golang中的匿名字段访问就是该字段对应的类型名称(实际上所谓的匿名字段,只不过是golang给这个字段直接命名为其类型对应的名称
)
如果匿名字段和当前结构体方法有同名的,那么调用该结构体的方法:
type Product struct {
price float64
name string
}
func (p Product) getInfo() string {
return fmt.Sprintf("%f %s",p.price,p.name)
}
type Address struct {
country,province,city,street string
}
func (address Address) getInfo() string {
return fmt.Sprintf("%s-%s-%s-%s",address.country,address.province,address.city,address.street)
}
type Order struct {
Product
Address
amount int
}
func (order Order) getInfo() string {
return "order"
}
type Order2 struct {
int
string
price float64
address string
}
func main() {
product := Product{name:"奥利奥",price:10.5}
address := Address{"中国","北京市","北京市","海淀区"}
var order Order
order.Product=product
order.Address=address
order.amount=10
fmt.Println(order.getInfo()) // order
}
这时候结构体和匿名字段拥有同样的方法,可以理解为面向对象的方法重写override