Go 面向对象(方法)

这篇具有很好参考价值的文章主要介绍了Go 面向对象(方法)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、 概述

在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数,这种带有接收者的函数,我们称为方法(method)。本质上,一个方法则是一个和特殊类型关联的函数。

一个面向对象的程序会用方法来表达其属性和对应的操作,这样使用这个对象的用户就不需要直接去操作对象,而是借助方法来做这些事情。

在Go语言中,可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法。

⽅法总是绑定对象实例,并隐式将实例作为第⼀实参 (receiver),方法的语法如下:

func (receiver ReceiverType) funcName (parameters) (results)
  • 参数 receiver 可任意命名。如⽅法中未曾使⽤,可省略参数名。
  • 参数 receiver 类型可以是 T 或 *T。基类型 T 不能是接⼝或指针。
  • 不支持重载方法,也就是说,不能定义名字相同但是不同参数的方法。

二、 为类型添加方法

1. 基础类型作为接收者

// 自定义类型,给int改名为MyInt
type MyInt int

// 在函数定义时,在其名字之前放上一个变量,即是一个方法
func (a MyInt) Add(b MyInt) MyInt {
  return a + b
}

//传统方式的定义
func Add(a, b MyInt) MyInt {//面向过程
  return a + b
}

func main() {
  var a MyInt=1
  var b MyInt=1

  //调用func (aMyInt) Add(bMyInt)
  fmt.Println("a.Add(b)=",a.Add(b))//a.Add(b)=2

  //调用func Add(a,bMyInt)
  fmt.Println("Add(a,b)=",Add(a,b))//Add(a,b)=2
}

通过上面的例子可以看出,面向对象只是换了一种语法形式来表达。方法是函数的语法糖,因为receiver其实就是方法所接收的第1个参数。

注意:虽然方法的名字一模一样,但是如果接收者不一样,那么方法就不一样。

2. 结构体作为接收者
方法里面可以访问接收者的字段,调用方法通过点.访问,就像`struct``里面访问字段一样:

type Person struct {
  name string
  sex byte
  age int
}

func (p Person) PrintInfo(){//给Person添加方法
  fmt.Println(p.name,p.sex,p.age)
}

func main() {
  p:=Person{"mike",'m',18}//初始化
  p.PrintInfo()//调用func(pPerson)PrintInfo()
}

打印结果为mike,m,18,你方法写的是Person那么这个方法只能传Person,不能传别的类型。

type student struct { //学生
	name string
	sex  string
	age  int
}

func (s student) Print() {
	s.age = 10
	fmt.Println(s)
}
func main() {
	stu := student{"赵云", "男", 30}
	stu.Print()
	fmt.Println(stu)
}

{赵云 男 10}
{赵云 男 30}

三、 值语义和引用语义

type Person struct {
  name string
  sex byte
  age int
}

// 指针作为接收者,引用语义
func (p *Person) SetInfoPointer(){
  // 给成员赋值
  (*p).name = "yoyo"
  p.sex = 'f'
  p.age = 22
}

// 值作为接收者,值语义
func (p Person) SetInfoValue(){
  // 给成员赋值
  p.name = "yoyo"
  p.sex = 'f'
  p.age = 22
}

func main() {
  // 指针作为接收者,引用语义
  p1 := Person{"mike",'m',18} // 初始化
  fmt.Println("函数调用前=",p1) // 函数调用前={mike10918}
  (&p1).SetInfoPointer()
  fmt.Println("函数调用后=",p1) // 函数调用后={yoyo10222}

  fmt.Println("==========================")

  p2 := Person{"mike",'m',18} // 初始化
  // 值作为接收者,值语义
  fmt.Println("函数调用前=",p2) // 函数调用前={mike10918}
  p2.SetInfoValue()
  fmt.Println("函数调用后=",p2) // 函数调用后={mike10918}
}

四、方法集

类型的方法集是指可以被该类型的值调用的所有方法的集合。

用实例实例value和pointer调用方法(含匿名字段)不受⽅法集约束,编译器编总是查找全部方法,并自动转换receiver实参。

1.类型*T方法集
一个指向自定义类型的值的指针,它的方法集由该类型定义的所有方法组成,无论这些方法接受的是一个值还是一个指针。

如果在指针上调用一个接受值的方法,Go语言会聪明地将该指针解引用,并将指针所指的底层值作为方法的接收者。

类型*T⽅法集包含全部receiver T + *T⽅法:

type Person struct{
  name string
  sex byte
  age int
}

// 指针作为接收者,引用语义
func (p *Person) SetInfoPointer(){
  (*p).name="yoyo"
  p.sex='f'
  p.age=22
}

// 值作为接收者,值语义
func (p Person) SetInfoValue(){
  p.name="xxx"
  p.sex='m'
  p.age=33
}

func main() {
  // p为指针类型
  var p*Person = &Person{"mike",'m',18}
  p.SetInfoPointer() // func (p)SetInfoPointer()

  p.SetInfoValue() // func (*p)SetInfoValue()
  (*p).SetInfoValue() // func (*p)SetInfoValue()
}

2. 类型T方法集
一个自定义类型值的方法集则由为该类型定义的接收者类型为值类型的方法组成,但是不包含那些接收者类型为指针的方法。

但这种限制通常并不像这里所说的那样,因为如果我们只有一个值,仍然可以调用一个接收者为指针类型的方法,这可以借助于Go语言传值的地址能力实现。

package main

import "fmt"

type Student struct {
  name string
  age int
}

// 指针作为接收者 引用语义
func (s *Student) SetStuPointer() {
  s.name = "Bob"
  s.age = 18
}

// 值作为接收者 值语义
func (s Student) SetStuValue() {
  s.name = "Peter"
  s.age = 18
}

func main() {
  // 指针作为接收者,引用语义
  s1 := Student{"Miller", 18} // 初始化
  fmt.Println("函数调用前 = ", s1) // 函数调用前 = {Miller 18}
  (&s1).SetStuPointer()
  fmt.Println("函数调用后 = ", s1) // 函数调用后 = {Bob 18}

  fmt.Println("==========================")

  s2 := Student{"mike", 18} // 初始化
  //值 作为接收者,值语义
  fmt.Println("函数调用前 = ", s2) // 函数调用前 = {mike 18}
  s2.SetStuValue() 
  fmt.Println("函数调用后 = ", s2) // 函数调用后 = {mike 18}
}
// 总结 : (引用语义:会改变结构体内容) (值语义:不会改变结构体内容)

五、 匿名字段

1. 方法的继承
如果匿名字段实现了一个方法,那么包含这个匿名字段的struct也能调用该方法。

type Person struct {
  name string
  sex byte
  age int
}

//Person定义了方法
func (p *Person) PrintInfo() {
  fmt.Printf("%s,%c,%d\n",p.name,p.sex,p.age)
}

type Student struct {
  Person//匿名字段,那么Student包含了Person的所有字段
  id int
  addr string
}

func main() {
  p := Person{"mike",'m',18}
  p.PrintInfo()

  s := Student{Person{"yoyo",'f',20},2,"sz"}
  s.PrintInfo()
}

也就是说我用student继承了person那么我就拥有了person的一切不管是字段,还是方法,我都能调用。

子类可以继承父类 可以继承属性和方法,但是父类不可继承子类的

// 子类
type student01 struct {
	student
	class int
}

func (s *student) PrintInfo() {
	fmt.Printf("编号%d\n", s.id)
	fmt.Printf("姓名%s\n", s.name)
	fmt.Printf("年龄%d\n ", s.age)
}
func main() {
	s := student{"赵云", 110, 30}
	s.PrintInfo()
	// 子类可以继承父类  可以继承属性和方法
	s1 := student01{student{"李白", 100, 20}, 9}
	s1.PrintInfo()
}

2. 方法的重写

type Person struct {
  name string
  sex byte
  age int
}
//Person定义了方法
func (p *Person) PrintInfo() {
  fmt.Printf("Person:%s,%c,%d\n",p.name,p.sex,p.age)
}
type Student struct {
  Person//匿名字段,那么Student包含了Person的所有字段
  id int
  addr string
}
//Student定义了方法
func (s *Student) PrintInfo() {
  fmt.Printf("Student:%s,%c,%d\n",s.name,s.sex,s.age)
}

func main() {
  p:=Person{"mike",'m',18}
  p.PrintInfo() //Person:mike,m,18
  s:=Student{Person{"yoyo",'f',20},2,"sz"}
  s.PrintInfo() //Student:yoyo,f,20
  s.Person.PrintInfo() //Person:yoyo,f,20
}

也就是说我调用了Person的方法,但是我觉得这个方法不行,然后我自己又重新写了个方法,最后调用student方法的时候就只会调用我这个方法,而不会调用person的方法了

六、 方法值和方法表达式

类似于我们可以对函数进行赋值和传递一样,方法也可以进行赋值和传递。

根据调用者不同,方法分为两种表现形式:方法值和方法表达式。两者都可像普通函数那样赋值和传参,区别在于方法值绑定实例,⽽方法表达式则须显式传参。

type Person struct {
	name string
	sex  byte
	age  int
}

func (p *Person) PrintInfoPointer() {
	fmt.Printf("%p,%v\n", p, p)
}
func (p Person) PrintInfoValue() {
	fmt.Printf("%p,%v\n", &p, p)
}

// 上面是定义的方法
func main() {
	p := Person{"mike", 'm', 18}
	p.PrintInfoPointer()         //0xc0420023e0,&{mike 109 18}
	pFunc1 := p.PrintInfoPointer //方法值,隐式传递 receiver
	pFunc1()                     //0xc0420023e0,&{mike 109 18}
	pFunc2 := p.PrintInfoValue
	pFunc2() //0xc042048420,{mike 109 18}
}

0xc0000603a0,&{mike 109 18}
0xc0000603a0,&{mike 109 18}
0xc000060420,{mike 109 18} 

2. 方法表达式文章来源地址https://www.toymoban.com/news/detail-689388.html

type Person struct {
	name string
	sex  byte
	age  int
}

func (p *Person) PrintInfoPointer() {
	fmt.Printf("%p,%v\n", p, p)
}
func (p Person) PrintInfoValue() {
	fmt.Printf("%p,%v\n", &p, p)
}

// 上面是定义的方法
func main() {
	p := Person{"mike", 'm', 18}
	p.PrintInfoPointer() //0xc0420023e0,&{mike 109 18}
	//方法表达式,须显式传参
	//func pFunc1 (p *Person))
	pFunc1 := (*Person).PrintInfoPointer
	pFunc1(&p) //0xc0420023e0,&{mike 109 18}
	pFunc2 := Person.PrintInfoValue
	pFunc2(p) //0xc042002460,{mike 109 18}
}


0xc00008e380,&{mike 109 18}
0xc00008e380,&{mike 109 18}
0xc00008e400,{mike 109 18} 

七、接口定义和使用

// 父类
type Operate struct {
	num1 int
	num2 int
}

// 加法子类
type Add struct {
	Operate
}

// 加法子类的方法
func (a *Add) Result() int {
	return a.num1 + a.num2
}

// 减法子类
type Sub struct {
	Operate
}

// 减法子类的方法
func (s *Sub) Result() int {
	return s.num1 - s.num2
}

// 上面是定义的方法
func main() {
	//创建加法对象
	var a Add
	a.num1 = 10
	a.num2 = 20
	v := a.Result()
	fmt.Println(v)
}

30


// 先定义接口  一般以er结尾 根据接口实现功能
type Humaner interface {
	// 方法  方法的声明
	sayhi()
}

type Student struct {
	name  string
	age   int
	score int
}

func (s *Student) sayhi() {
	fmt.Printf("大家好,我是%s,今年%d岁,我的成绩%d分\n", s.name, s.age, s.score)
}

type Teacher struct {
	name    string
	age     int
	subject string
}

func (t *Teacher) sayhi() {
	fmt.Printf("大家好,我是%s,今年%d岁,我的学科是%s\n", t.name, t.age, t.subject)
}

// 上面是定义的方法
func main() {
	var h Humaner
	stu := Student{"小明", 18, 98}
	// 将对象信息赋值给接口类型变量
	h = &stu
	h.sayhi()
	//stu.sayhi()
}

大家好,我是小明,今年18岁,我的成绩98

到了这里,关于Go 面向对象(方法)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Go 基础篇】走进Go语言的面向对象编程世界

    欢迎各位编程爱好者们!今天我们将进入Go语言的面向对象编程(OOP)世界,一窥这门语言如何运用OOP思想来组织和构建程序。无论你是初学者还是有一些经验的开发者,本文都将为你揭示Go语言中的OOP特性、方法和最佳实践。 面向对象编程是一种程序设计范式,它以对象为

    2024年02月10日
    浏览(35)
  • Go 面向对象(方法)

    在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数,这种带有接收者的函数,我们称为方法(method)。本质上,一个方法则是一个和特殊类型关联的函数。 一个面向对象的程序会用方法来表达其属性和对应的操作,这样使用这个

    2024年02月10日
    浏览(25)
  • 【Go语言快速上手(四)】面向对象的三大特性引入

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:Go语言专栏⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习更多Go语言知识   🔝🔝 GO语言也支持面向对象编程,但是和传统的面向对象语言(如CPP)有明显的区别,GO并不是纯粹的面对对象编程语言.所以说GO是支持面向对

    2024年04月26日
    浏览(37)
  • 【Golang】go编程语言适合哪些项目开发?

    前言 在当今数字化时代,软件开发已成为各行各业的核心需求之一。 而选择适合的编程语言对于项目的成功开发至关重要。 本文将重点探讨Go编程语言适合哪些项目开发,以帮助读者在选择合适的编程语言时做出明智的决策。 Go 编程语言适合哪些项目开发? Go是由Google开发

    2024年02月04日
    浏览(60)
  • 【Golang】VsCode下开发Go语言的环境配置(超详细图文详解)

    📓推荐网站(不断完善中):个人博客 📌个人主页:个人主页 👉相关专栏:CSDN专栏、个人专栏 🏝立志赚钱,干活想躺,瞎分享的摸鱼工程师一枚 ​ 话说在前,Go语言的编码方式是 UTF-8 ,理论上你直接使用文本进行编辑也是可以的,当然为了提升我们的开发效率我们还是需

    2024年02月07日
    浏览(70)
  • Golang 的面向对象

    duck typing golang中实现某个接口不需要像其它语言使用 implemet 去继承实现,而是只要你的结构体包含接口所需的方法即可 nil不一定是空接口 初始化的接口是等于 nil的,接口底层其实有一个type来记录原始的struct,当某个struct赋值给接口时,接口会在type中记录该类型(就算是

    2024年02月13日
    浏览(31)
  • Golang之路---03 面向对象——类型断言

    作用 检查 i 是否为 nil 检查 i 存储的值是否为某个类型 使用方式 第一种: 这个表达式可以 断言一个接口对象(i)里不是 nil,并且接口对象(i)存储的值的类型是 T ,如果断言成功,就会返回值给 t,如果断言失败,就会触发 panic。(即nil或者接口对象类型不为T时均会触发

    2024年02月14日
    浏览(32)
  • 【 Python 全栈开发 ⑰ 】面向对象、继承、多态、类方法与类属性

    Python 面向对象编程 是一种编程范式,它将数据和操作数据的方法组合在一起,形成一个“对象”,并且该对象可以被复用。在 Python 中,每一个对象都拥有一些属性和方法,它们是该对象的特征和行为。 面向对象编程可以帮助我们更好地组织和管理代码,使得代码更加模块

    2024年02月08日
    浏览(35)
  • 【Go】四、面向对象

    ​        go只提供 类型而不写字段名的方式 ,也就是匿名字段,也称为嵌入字段 1、不写字段名,只给出类型 2、嵌套结构中,包含相同字段名,逐级加入 ​        接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节

    2023年04月23日
    浏览(16)
  • 使用 Go (Golang) 使用 OpenCV 绘制对象 GoCV

    本文将向你展示如何使用 OpenCV 使用 Go 编程语言 (Golang) 和 GoCV 包绘制直线、正方形、圆形和椭圆形对象。 OpenCV 是一个主要用于实时计算机视觉的编程函数库。最初由 Intel 开发,然后由 Willow Garage 和 Itseez 提供支持。这个库是跨平台的,可以在开源 Apache 2 许可下免费使用。

    2024年02月07日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包