go语言方法集为类型添加方法示例解析

目录
  • 1概述
  • 2为类型添加方法
    • 2.1基础类型作为接收者
    • 2.2结构体作为接收者
  • 3值语义和引用语义
  • 4方法集
    • 4.1类型 *T 方法集
    • 4.2类型 T 方法集
  • 5匿名字段
    • 5.1方法的继承
    • 5.2方法的重写
  • 6方法值和方法表达式
    • 6.1方法值
    • 6.2方法表达式

1概述

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

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

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

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

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

2为类型添加方法

2.1基础类型作为接收者

type MyInt int//自定义类型,给int改名为MyInt
//在函数定义时,在其名字之前放上一个变量,即是一个方法
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   // 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.2结构体作为接收者

方法里面可以访问接收者的字段,调用方法通过点(. )访问,就像struct里面访问字段一样:

package main
import "fmt"
func main(){
	jeff:=user{1,"jeff",18,"上海"}
	fmt.Println(jeff.Add(10))
}
// 相当于定义user类
type user struct {
	id int
	name string
	age int
	addr string
}
// 添加Add方法,接收参数num
func (p user) Add(num int)int{
	fmt.Println(p.age)
	return p.age+num
}

3值语义和引用语义

package main
import "fmt"
func main() {
	//指针作为接收者,引用语义
	jeff := Person{"jeff","男",18}//初始化
	fmt.Println("函数调用前=",jeff)//函数调用前= {jeff 男 18}
	(&jeff).Add()  // 生效,(&jeff)拿到jeff的地址(指针)
	//jeff.Add()  // 修改不生效
	fmt.Println("函数调用后=",jeff)//函数调用后= {aaa 女 22}
	fmt.Println("==========================")
	chary := Person{"chary","女",18}//初始化
	//值作为接收者,值语义
	fmt.Println("函数调用前=",chary)//函数调用前= {chary 女 18}
	chary.Add2()  //不生效
	//(&chary).Add()
	fmt.Println("函数调用后=",chary)//函数调用后= {chary 女 18}
}
type Person struct {
	name string
	sex string
	age int
}
//指针作为接收者,引用语义
func (p *Person) Add(){
	//给成员赋值
	(*p).name = "aaa"
	p.sex = "女"
	p.age = 22
}
//值作为接收者,值语义
func (p Person) Add2(){
	//给成员赋值
	p.name = "bbb"
	p.sex = "男"
	p.age = 22
}

4方法集

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

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

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

4.2类型 T 方法集

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

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

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.SetInfoPointer()    //func(&p)SetInfoPointer()
    p.SetInfoValue()    //func(p)SetInfoValue()
    (&p).SetInfoValue()    //func(*&p)SetInfoValue()
}

5匿名字段

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

5.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
}

6方法值和方法表达式

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

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

6.1方法值

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

6.2方法表达式

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

以上就是go语言方法集以及为类型添加方法的示例解析的详细内容,更多关于go语言方法集类型添加方法的资料请关注我们其它相关文章!

(0)

相关推荐

  • Golang动态调用方法小结

    main.go package main import ( "lenu/call" "reflect" ) type FuncCollection map[string]reflect.Value func main() { _, _ = CallFunc("Hello", "执行Hello方法") _, _ = CallFunc("World", "执行World方法") } func

  • go实现一个分布式限流器的方法步骤

    目录 1. 接口定义 2. LocalCounterLimiter 3. LocalTokenBucketLimiter 4. RedisCounterLimiter 5. RedisTokenBucketLimiter 项目中需要对 api 的接口进行限流,但是麻烦的是,api 可能有多个节点,传统的本地限流无法处理这个问题.限流的算法有很多,比如计数器法,漏斗法,令牌桶法,等等.各有利弊,相关博文网上很多,这里不再赘述. 项目的要求主要有以下几点: 支持本地/分布式限流,接口统一 支持多种限

  • Go interface{} 转切片类型的实现方法

    遇到这样一个情况想将变量v转化为[]string类型 var v interface{} a := []interface{}{"1", "2"} v = a // v 这时还是interface{} 但其实是个 []interface{} newValue := v.([]string) fmt.Println(newValue) 提示: panic: interface conversion: interface {} is []interface {}, no

  • Go 实现 Nginx 加权轮询算法的方法步骤

    目录 一,Nginx 负载均衡的轮询 (round-robin) 1. nginx 中的配置 2. 简单介绍 3. 特点 4. 实现 (这里使用golang模拟实现) 5. 测试 二,Nginx 负载均衡的加权轮询 (weighted-round-robin) 1. nginx 配置 2. 加权算法简介-特点 3. 算法说明 4. 简单举例 5. 代码实现 6. 测试验证 最近在看一些 getway 相关的资料,发现有关 Nginx 负载均衡的算法有点多,但是有点乱,所以整理下...如有不对地方

  • Golang中Map按照Value大小排序的方法实例

    目录 起因 探索 实现 第一步 第二步 第三步 总结 总结 Golang中的 map 默认是 无序的 . 起因 最近项目中有这样一个需求: 根据用户当前的坐标点,获取该用户附近的预设城市名称. 这里有一个注意点是,假设这些支持的城市名称是预设的,所以就不能直接通过地图类api根据坐标点获取所在城市名称了. 想到的解决思路是: 获取这几个预设城市的坐标点 App端获取用户当前坐标点 分别计算得到该用户坐标点距离各个预设城市的坐标点距离 然后计算得到其中距离最小的一项 这个坐标点对应的城市就是所求

  • go语言方法集为类型添加方法示例解析

    目录 1概述 2为类型添加方法 2.1基础类型作为接收者 2.2结构体作为接收者 3值语义和引用语义 4方法集 4.1类型 *T 方法集 4.2类型 T 方法集 5匿名字段 5.1方法的继承 5.2方法的重写 6方法值和方法表达式 6.1方法值 6.2方法表达式 1概述 在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数,这种带有接收者的函数,我们称为方法(method).本质上,一个方法则是一个和特殊类型关联的函数. 一个面向对象的程序会用方法来表达其属性

  • 浅谈Java泛型让声明方法返回子类型的方法

    泛型典型的使用场景是集合.考虑到大多数情况下集合是同质的(同一类型),通过声明参数类型,可免去类型转换的麻烦.本文将讨论本人阅读Spring Security源码时遇到的一个关于泛型递归模式的问题. 声明方法返回子类型 在Spring Security的源码里有一个ProviderManagerBuilder接口,声明如下 public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>> ext

  • Java 8 Stream操作类型及peek示例解析

    简介 java 8 stream作为流式操作有两种操作类型,中间操作和终止操作.这两种有什么区别呢? 我们看一个peek的例子: Stream<String> stream = Stream.of("one", "two", "three","four"); stream.peek(System.out::println); 上面的例子中,我们的本意是打印出Stream的值,但实际上没有任何输出. 为什么呢? 中间

  • goalng 结构体 方法集 接口实例详解

    目录 一 前序 二 事出有因 errors.As 方法签名 三 结构体与实例的数据结构 1. 结构体类型 2. 实例 3 方法调用 3.1 方法表达式 3.2 值实例调用所有方法 3.3 指针实例调用所有方法 3.4 空指针无法调用值方法 四 接口 1 接口数据结构 2 接口赋值 值方法集 指针方法集 总结 一 前序 很多时候我们以为自己懂了,但内心深处却偶有困惑,知识是严谨的,偶有困惑就是不懂,很幸运通过大量代码的磨练,终于看清困惑,并弄懂了. 本篇包括结构体,类型, 及 接口相关知识,希望对

  • Go语言中如何通过方法为类型添加行为

    前言 数十年以来, 传统的面向对象语言总是说方法属于类, 但 Go 不是这样做的: 它提供了方法, 但是并没有提供类和对象. 乍一看, 这种做法似乎有些奇怪, 甚至可以说有点儿疯狂, 但实际上 Go 的方法比以往其他语言的方法都要灵活. 使用 kelvinToCelsius . celsiusToFahrenheit . fahrenheitToCelsius . celsiusToKelvin 这样的函数虽然也能够完成温度转换工作, 但是通过声明相应的方法并把它们放置到属于自己的地方, 能够让

  • 详解Swift中对C语言接口缓存的使用以及数组与字符串转为指针类型的方法

    详解Swift中对C语言接口缓存的使用以及数组与字符串转为指针类型的方法 由于Swift编程语言属于上层编程语言,而Swift中由于为了低层的高性能计算接口,所以往往需要C语言中的指针类型,由此,在Swift编程语言刚诞生的时候就有了UnsafePointer与UnsafeMutablePointer类型,分别对应为const Type*类型与Type *类型. 而在Swift编程语言中,由于一般数组(Array)对象都无法直接用于C语言中含有指针类型的函数参数(比如:void*),所以往往需要

  • Go语言七篇入门教程三函数方法及接口

    目录 1. 函数 2. 方法 3. 接口 如何学习Go 参考书籍: <go语言程序设计> 1. 函数 每个函数声明都包含一个名字,一个形参列表,一个可选的返回列表以及函数体: func name(parameter-list)(result-list){ body } 形参列表:指定另一组变量的参数名和参数类型,这些局部变量都由调用者提供的提供的实参传递而来的. 返回列表:指定了函数返回值的类型.当函数返回一个未命名的返回值或者没有返回值的时候,返回列表的圆括号可以忽略. func FanOn

  • 判断一个变量是数组Array类型的方法

    在很多时候,我们都需要对一个变量进行数组类型的判断.JavaScript中如何判断一个变量是数组Array类型呢?我最近研究了一下,并分享给大家,希望能对大家有所帮助. JavaScript中检测对象的方法 1.typeof操作符 这种方法对于一些常用的类型来说那算是毫无压力,比如Function.String.Number.Undefined等,但是要是检测Array的对象就不起作用了. 复制代码 代码如下: alert(typeof null); // "object" alert

  • Swift中非可选的可选值类型处理方法详解

    前言 在我们使用objective-c表示字符串信息的时候,可以用下面方法书写. NSString *str = @"秋恨雪"; str = nil; 因为objective-c是弱类型语言,所以这里的str既可以是具体的字符串也可以是nil.但到了Swift中就不可以了,因为Swift是类型安全的语言,一个String类型的变量不可能既能是具体的字符串,又可以为nil(更严格的说String类型的内容只能是字符串).所以,在Swift中有了可选类型的概念.(其实这一概念也是"

  • 详解易语言调用js实现md5加密方法

    易语言调用js需要用到拓展组件的脚本组件, 在窗口创建完毕的事件里给脚本组件初始化设置下脚本组件的语言属性,在这里以JScript为例: 脚本组件执行脚本的简单方法是:  脚本组件1.执行 ()  然后根据需要调用的脚本函数   脚本组件1.运行 () 首先看下脚本组件1.执行 () 的参数和返回值: 调用格式: 〈逻辑型〉 对象.执行 (文本型 脚本代码) - 脚本语言支持组件->脚本组件 英文名称:Execute 执行指定的代码文本.返回真为执行正常,返回假为出错,错误信息可以从"错误

随机推荐