目录
go语言中没有类的概念,使用结构体来代替类,所以也可以实现类的三种特性:
封装:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。封装是面向对象的特征之一,是对象和类概念的主要特性。
继承:就是的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为
多态:同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作。
【1】封装
这里写了一个staff的包来实现封装的特性, 里面定义了一个staff的结构体,其中Name是公开的,id和sal是私有的,只能通过GetXxx和SetXxx方法访问和修改
package staff
import "fmt"
//定义一个员工的结构体,其中工号和薪水是保密的不能被其他包访问
type staff struct {
Name string
id string
sal float64
}
//定义一个函数来返回一个员工的实例化对象
func NewStaff(name string) *staff {
return &staff{
Name: name,
}
}
//定义两对SetXxx和GetXxx方法来管理工号和薪水
func (s *staff) GetId() string {
return s.id
}
func (s *staff) SetId(id string) {
if len(id) != 6 {
fmt.Println("工号长度必须为6位")
return
}
s.id = id
}
func (s *staff) GetSal() float64 {
return s.sal
}
func (s *staff) SetSal(sal float64) {
if sal < 3000 || sal > 30000 {
fmt.Println("薪水范围错误")
return
}
s.sal = sal
}
调用staff包
package main
import (
"fmt"
"gocode/struct/staff"
)
func main() {
staff1 := staff.NewStaff("tom")
staff1.SetId("100001")
staff1.SetSal(6000)
id := staff1.GetId()
sal := staff1.GetSal()
fmt.Printf("员工名字是%v 工号%v 薪水%v元", staff1.Name, id, sal)
}
【2】继承
在Golang中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性。
【2.1】嵌套基本语法
//商品结构体,每个商品都有名称和价格
type Goods struct {
Name string
Price float64
}
//书本结构体,拥有商品的所有字段和自己独有的作者字段
type Book struct {
Goods //这里就是嵌套匿名结构体Goods
Writer string
}
【2.2】字段继承
package main
import "fmt"
//商品结构体,每个商品都有名称和价格
type Goods struct {
Name string
Price float64
}
//书本结构体,拥有商品的所有字段和自己独有的作者字段
type Book struct {
Goods //这里就是嵌套匿名结构体Goods
Writer string
}
func main() {
book1 := Book{}
book1.Name = "三体" //可以直接给goods中的一个字段赋值
book1.Goods.Price = 88.5 //也可以通过goods来赋值
book1.Writer = "刘慈欣"
fmt.Println("商品名", book1.Name) //可以直接获取goods的字段
fmt.Println("价格", book1.Goods.Price) //也可以通过goods获取
fmt.Println("作者", book1.Writer)
}
【2.3】方法继承
package main
import "fmt"
//商品结构体,每个商品都有名称和价格
type Goods struct {
Name string
Price float64
}
//书本结构体,拥有商品的所有字段和自己独有的作者字段
type Book struct {
Goods //这里就是嵌套匿名结构体Goods
Writer string
}
//给商品加一个方法 可以被卖出
func (g *Goods) SellOut() {
fmt.Println(g.Name, "卖出去了")
}
//给书籍加一个方法 可以被阅读
func (b *Book) Read() {
fmt.Println("阅读了", b.Goods.Name)
}
func main() {
book1 := Book{}
book1.Name = "三体" //可以直接给goods中的一个字段赋值
book1.Goods.Price = 88.5 //也可以通过goods来赋值
book1.Writer = "刘慈欣"
book1.Goods.SellOut() //方法继承,书籍继承了商品的卖出方法,也可以被卖出了
book1.Read()
}
【2.4】方法和字段重载
当结构体和匿名结构体有相同的字段或者方法时编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分
package main
import "fmt"
//商品结构体,每个商品都有名称和价格
type Goods struct {
Name string
Price float64
}
//书本结构体,拥有商品的所有字段和自己独有的作者字段
type Book struct {
Goods //这里就是嵌套匿名结构体Goods
Name string //重写字段
Writer string
}
//给商品加一个方法 可以被卖出
func (g *Goods) SellOut() {
fmt.Println(g.Name, "卖出去了")
}
//给书籍加一个方法 可以被阅读
func (b *Book) Read() {
fmt.Println("阅读了", b.Goods.Name)
}
//给书籍也加一个卖出方法
func (b *Book) SellOut() {
b.Name = "三体3:死神永生"
fmt.Printf("有人阅读了《%v》并买走了这本书\n", b.Name)
}
func main() {
book1 := Book{}
book1.Goods.Name = "三体"
book1.Goods.Price = 88.5
book1.Writer = "刘慈欣"
book1.Goods.SellOut() //方法继承,书籍继承了商品的卖出方法,也可以被卖出了
book1.Read()
book1.SellOut() //方法重载,书籍重写了卖出方法
//这里编译器会优先去Book中找SellOut方法,如果找到了就直接执行,如果没找到则会去Goods中找
fmt.Println(book1.Name)
}
【2.5】注意事项
1、结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错。
2、如果一个struct嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字,否则编译报错。
3、嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值。
package main
import "fmt"
type A struct {
Name string
Price float64
}
type B struct {
Name string
Price float64
}
type C struct {
A
B
}
func main() {
//方式一 按照顺序赋值
var c = C{A{"tom", 1.0}, B{"jack", 10.1}}
//方式二 按照字段名赋值,不固定顺序
var c2 = C{
A{
Name: "lucy",
Price: 5.0,
},
B{
Price: 10.0,
Name: "jane",
},
}
fmt.Println("c", c)
fmt.Println("c2", c2)
}
【3】多态
变量(实例)具有多种形态。面向对象的第三大特征,在Go语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。
示例代码:
下面声明了一个run接口,里面声明start和end方法。
然后rabbit和turtle结构体都定义了start和end方法。
competition结构体定了running方法,传的形参是run接口,方法里面直接调用了start和end。
running方法的run变量可以根据传入的实参而输出不同的内容,体现了多态的特性。
package main
import "fmt"
type Run interface {
Start()
End()
}
type Rabbit struct {
}
func (r Rabbit) Start() {
fmt.Println("兔子跑步开始")
}
func (r Rabbit) End() {
fmt.Println("兔子跑步结束")
}
type Turtle struct {
}
func (t Turtle) Start() {
fmt.Println("乌龟跑步开始")
}
func (t Turtle) End() {
fmt.Println("乌龟跑步结束")
}
type Competition struct {
}
func (c Competition) Running(run Run) {
run.Start()
run.End()
}
func main() {
competition1 := Competition{}
rabbit1 := Rabbit{}
turtle1 := Turtle{}
competition1.Running(rabbit1)
fmt.Println("-----------------------")
competition1.Running(turtle1)
}
【3.1】数组的多态体现
go语言中 ,数组只能存放一种数据类型。但是可以通过接口来实现存储不同的数据类型。
只要这些数据类型实现了该接口就可以存到这个数组中。
代码示例:
下面的代码定义了一个[3]AInterface类型的数组arr。
B和C都实现了该接口,所以这两个结构体都可以存入arr数组中。
package main
import "fmt"
type AInterface interface {
TestA() string
}
type B struct {
Name string
}
type C struct {
Name string
}
func (b *B) TestA() string {
fmt.Println(" TestA() ----- B")
return b.Name
}
func (c *C) TestA() string {
fmt.Println(" TestA() ----- C")
return c.Name
}
func main() {
var arr [3]AInterface
arr[0] = &B{"tom"}
arr[1] = &B{"jack"}
arr[2] = &C{"mary"}
for _,v := range arr{
fmt.Println("name = ",v.TestA())
}
}