golang学习笔记--面向对象三大特性

文章详细介绍了Go语言如何通过结构体实现封装、继承和多态等面向对象编程的关键特性。封装通过限制对数据的直接访问,使用公共方法来管理私有字段。继承则是通过结构体嵌套匿名结构体实现,可以继承字段和方法。多态则通过接口的使用,允许不同类型的实例遵循同一接口并执行相应操作。文中还给出了具体的代码示例,包括数组的多态体现。
摘要由CSDN通过智能技术生成

目录

【1】封装

【2】继承

【2.1】嵌套基本语法

【2.2】字段继承

【2.3】方法继承

【2.4】方法和字段重载

【2.5】注意事项 

【3】多态

【3.1】数组的多态体现


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())
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值