Golang——方法

一. 方法定义

        Golang方法总是绑定对象的实例,并隐式将实例作为第一实参。

  • 只能为当前包内命名类型定义方法
  • 参数receiver可以任意命名。如方法中未曾使用,可省略参数名
  • 参数receiver类型可以是T或*T。基类型T不能是接口或指针类型(即多级指针)
  • 不支持方法重载,receiver只是参数前面的组成部分
  • 可用实例value或pointer调用全部方法,编译器自动转换

        一个方法就是一个包含接收者(receiver)的函数,接收者(receiver)可以是命名类型或者结构体类型的一个值或者是一个指针。

        所有给定类型的方法属于该类型的方法集。

        1.1 方法定义

func (reveicer type) methodName(参数列表)(返回值列表){}

//参数和返回值可以省略
package main

type test struct{}

// 接收者为值类型,无参,无返回值
func (t test) method0() {}

// 接收者为值类型,单参,无返回值
func (t test) method1(x int) {}

// 接收者为值类型,无参,单返回值
func (t test) method2() (res int) { return }

// 接收者为值类型,多参,无返回值
func (t test) method3(x, y int) {}

// 接收者为值类型,无参,多返回值
func (t test) method4() (res1, res2 int) { return }

// 接收者为值类型,多参,多返回值
func (t test) method5(x, y int) (res1, res2 int) { return }

// 接收者为指针类型,无参,无返回值
func (t *test) method6() {}

// 接收者为指针类型,单参,无返回值
func (t *test) method7(x int) {}

// 接收者为指针类型,无参,单返回值
func (t *test) method8() (res int) { return }

// 接收者为指针类型,多参,无返回值
func (t *test) method9(x, y int) {}

// 接收者为指针类型,无参,多返回值
func (t *test) method10() (res1, res2 int) { return }

// 接收者为指针类型,多参,多返回值
func (t *test) method11(x, y int) (res1, res2 int) { return }

func main(){}

下面定义了一个结构体类型和它的方法。

        解释:首先我们定义了一个叫做User的结构体类型,然后定义了一个该类型的方法Notice,该方法的接收者是一个User类型的值。要调用Notice方法,我们需要一个User类型的值或者指针。 

        在例子中,当我们使用指针时,Go调整成解引用使得调用可以被执行。

        注意:当接收者不是一个指针时,该方法操作对应接收者值的副本。意思是即使你使用指针调用方法,但方法的接收者是值类型,方法内部的操作还是对副本进行操作,不会修改原来的值。

         我们修改Notice方法,让他的接收者变成指针类型。

        注意:当接收者是指针,即使使用值类型调用方法,方法内部也是对指针操作。会修改原来的值。

  • 方法不过是特殊的函数,只需要将其还原,就知道receiver T和*T的差别

        1.2 普通函数与方法区别

  • 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然
  • 对于方法,接收者为值类型,可以直接使用指针类型的变量调用方法,反过来也可以。
package main

import "fmt"

//普通函数
//接收者为值类型
func valueIntTest(a int) int {
	return a + 10
}

//接收者为指针类型
func pointIntTest(a *int) int {
	return *a + 10
}

func testValue() {
	a := 10
	fmt.Println(valueIntTest(a))
	//不能传递指针 如:valueIntTest(&a)

	b := 20
	fmt.Println(pointIntTest(&b))
	//不能传递值 如:pointIntTest(b)
}

//结构体
type Data struct {
	x int
}

func (d Data) valueShowData() {
	fmt.Println(d.x)
}

func (d *Data) pointShowData() {
	fmt.Println(d.x)
}

func testStruct() {
	//值类型调用方法
	d1 := Data{x: 10}
	d1.valueShowData()
	d1.pointShowData()

	//指针类型调用方法
	d2 := &Data{x: 100}
	d2.valueShowData()
	d2.pointShowData()
}

func main() {
	testValue()
	testStruct()
}

二. 匿名字段

        Golang匿名字段:可以像字段成员那样访问匿名字段方法,编译器负责查找。

        Go语言是通过嵌套来实现继承的,依据编译器查找次序,只需要在外层定义同名方法,就可以实现继承的隐藏。

三. 方法集

        Golang的方法集:每个类型都有与之关联的方法集,这会影响到接口的实现规则。

  • 类型T方法集包含全部receiver T的方法
  • 类型*T方法集包含全部receiver T + *T方法
  • 如类型S包含匿名字段T,则S和*S方法集包含T的方法
  • 如类型S包含匿名字段*T,则S和*S方法集包含T + *T方法
  • 不管嵌入T或*T,*S方法集总是包含T + *T方法

        用实例value和pointer调用方法(含其它自定义类型)不受方法集的约束,编译器总是查找全部方法,并自动转换reveiver实参。

        方法集和调用方法没有关系。方法集是Go语言中实现面向对象编程特性的关键概念之一,它允许开发者为自定义类型定义行为,并通过接口来实现多态。同时,方法集也是Go语言类型系统和接口系统中不可或缺的一部分。

  • Go语言中内部类型方法提升的规则

        类型T方法集包含全部receiver T的方法。

        类型*T方法集包含全部receiver T + *T方法。

        给定结构体类型S和一个命名为T的类型,方法提升像下面规定的这样被包含在结构体方法集中:

        如类型S包含匿名字段T,则S和*S方法集包含T方法。

        这条规则说的是当我们嵌入一个值类型嵌入类型的接收者为值类型的方法将被提升,可以被外部类型的值和指针调用。

        如类型S包含匿名字段*T,则S和*S方法集包含T+*T方法。

        这条规则说的是当我们嵌入一个类型的指针,嵌入类型的接收者为值类型或者指针类型的方法将被提升,可以被外部类型的值或指针调用

四. 表达式

        Golang表达式:根据调用者不同,方法分为两种表现形式。

  1. object.method(arg...)
  2. type.method(arg...)

        前者称为method value,后者method expression。两者都可像普通函数那样赋值和传参,区别在于method value绑定实例,而method expression则需显示传参。

  •  method value会复制receiver

在汇编层面上,method value和闭包的实现方式相同,实际返回FuncVal类型对象。

FuncVal { method_address, receiver_copy }
  • 可依据方法集转换method expression,注意receiver类型的差异

  • 将方法还原成函数,就是下面的代码
package main

type User struct{}

func (User) TestValue() {}

func (*User) TestPoint() {}

func main() {
	var u *User = nil
	u.TestPoint()

	(*User)(nil).TestPoint() //method value,对象为nil
	(*User).TestPoint(nil)   //method expression,传的对象为nil
}

五. 自定义error

        5.1 抛异常和处理异常

        5.1.1 抛异常

package main

import "fmt"

//系统抛异常
func test01() {
	a := [5]int{1, 2, 3, 4}
	a[1] = 123
	fmt.Println(a)

	index := 10
	a[index] = 10
	fmt.Println(a)
}

func getCircleArea(radius float32) float32 {
	if radius < 0 {
		panic("半径不能为负") //自己抛异常
	}

	return 3.14 * radius * radius
}

func test02() {
	getCircleArea(-5)
}

//捕获异常
func test03() {
	//回收异常
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	test02()
	fmt.Println("这里不执行")
}

func test04() {
	test03()
	fmt.Println("test04")
}

func main() {
	test04()

	test01()
}

        5.1.2 返回异常

        5.2 自定义错误

 

 

 

        

 

  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值