go语言中通常是为自定义类型定义方法,同时要求方法的定义和类型的定义必须在同一个包内。(不能为int、bool等预声明类型定义方法,因为他们的作用域是全局)

首先介绍一下c/c++与go语言结构体指针的区别:

#include <iostream>
#include<map>
using namespace std;

struct T{
    int a;
};

int main(){
    T t=T{1};
    T *p=&t;
    cout<<p->a<<endl;
    cout<<(*p).a<<endl;

    return 0;
}

在c语言中,结构体指针访问结构体字段可以通过p->a或者(*p).a,但是在go语言中没有->

package main

import "fmt"

type T1 struct{
	a int
}
func main(){
	i:=&T1{a:100}
	fmt.Println(i.a)
	fmt.Println((*i).a)
}

可以看出通过i.a和(*i).a都可以访问,这是因为内部编译器将i.a转换成(*i).a。(隐式间接引用)

方法值和方法表达式

package main

import "fmt"

type T1 struct{
	a int
}
func (t T1)f1(i int){
	fmt.Println(t.a+i)
}
func main(){
	t:=T1{1}
	//1)a、方法值调用
	f:=t.f1
	f(1)
	//1)b、方法值调用
	t.f1(1)
	//2)a、方法表达式调用
	f1:=(T1).f1
	f1(t,1)
	//2)b、方法表达式调用
	(T1).f1(t,1)
}

方法值类似于方法接收者,方法表达式类似于作为函数的第一个默认参数。

方法集

将接受者为值类型T的方法的集合记录为S,将接受者为指针类型T的方法的集合记录为S则:
1)T类型的方法集是S
2)T类型的方法集为S何S

在直接使用类型实例t(t T)调用方法是,无论是值类型还是指针类型,都可以调用类型的所有方法,编译器进行转换。

package main

import "fmt"

type T struct{
	a int
}

func(t T) get()int{
	return t.a
}

func(t *T)set(i int){
	t.a=i
}

func main(){
	var a=10
	p:=&a
	fmt.Println(p)
	i:=&T{}
	i1:=T{}
	i.set(10)
	i1.set(100)
	fmt.Println(i.get())
	fmt.Println((*i).get())
	fmt.Println(i1.get())
}

当通过类型字面量显示的进行方法调用,编译器无法转换

type A struct{}
func (A)f1(){}
func (*A)f2(){}
//*A的方法集是f1和f2
(*A)(&struct{}{}).f1()
(*A)(&struct{}{}).f2()//方法值调用
(*A).f1((*A)(&struct{}{})) //方法表达式调用
//A的方法集是f1
(A)(struct{}{}).f1()
//(A)(struct{}{})f2() ❎方法集不匹配