写 Golang 代码要掌握的知识

79 阅读2分钟

所有的参数传递都是值传递

  • 其中引用类型变量存放的是内存地址值,传递的值自然也是内存地址值。
  • 引用类型包括切片、映射、通道、接口、函数。
package demo

import (
	"fmt"
	"testing"
)

type Record struct {
	Field string
}

func Call(record Record) {
	record.Field = "recordValue"

	fmt.Println("record.Field = ", record.Field)
}

func Test(t *testing.T) {
	var record1 Record = Record{Field: "record1Value"}
	var record2 Record = record1

	record2.Field = "record2Value"
	Call(record2)

	fmt.Println("record1.Field = ", record1.Field)
	fmt.Println("record2.Field = ", record2.Field)
}

输出结果:

record.Field =  recordValue
record1.Field =  record1Value
record2.Field =  record2Value

切片类型作为参数传递的问题

调用 append 函数往切片追加元素时,可能会生成一个新的切片。

package demo

import (
	"fmt"
	"testing"
)

func Call(numbers []int) []int {
	numbers = append(numbers, 1)
	numbers = append(numbers, 2)
	numbers = append(numbers, 3)

	return numbers
}

func Test(t *testing.T) {
	var numbers1 = []int{0}

	numbers2 := Call(numbers1)

	fmt.Println("numbers1 = ", numbers1)
	fmt.Println("numbers2 = ", numbers2)
}

输出结果:

numbers1 =  [0]
numbers2 =  [0 1 2 3]

空接口 interface{} 并不像 C/C++ 中的 void*类型指针

空接口 interface{} 指向的结构体存放变量的类型和变量的内存地址值。

package demo

import (
	"fmt"
	"testing"
)

func Test(t *testing.T) {
	var str *string = nil
	var any1 interface{} = str
	var any2 interface{} = nil
	var any3 interface{} = any1

	{
		_, ok := any1.(*string)

		fmt.Println("_, ok := any1.(*string); ok =", ok)
	}

	{
		_, ok := any2.(*string)

		fmt.Println("_, ok := any2.(*string); ok =", ok)
	}

	{
		_, ok := any3.(*string)

		fmt.Println("_, ok := any3.(*string); ok =", ok)
	}
}

输出结果:

_, ok := any1.(*string); ok = true
_, ok := any2.(*string); ok = false
_, ok := any3.(*string); ok = true

值接收者的方法指向的是副本

package demo

import (
	"fmt"
	"testing"
)

type Record struct {
	Field string
}

func (record Record) Call1(field string) {
	record.Field = field
}

func (record *Record) Call2(field string) {
	record.Field = field
}

func Test(t *testing.T) {
	var record1 Record = Record{Field: "record1Value"}
	record1.Call1("record1NewValue")
	fmt.Println("record1.Field = ", record1.Field)

	var record2 Record = Record{Field: "record2Value"}
	(&record2).Call1("record2NewValue")
	fmt.Println("record2.Field = ", record2.Field)

	var record3 Record = Record{Field: "record3Value"}
	record3.Call2("record3NewValue")
	fmt.Println("record3.Field = ", record3.Field)

	var record4 Record = Record{Field: "record4Value"}
	(&record4).Call2("record4NewValue")
	fmt.Println("record4.Field = ", record4.Field)
}

输出结果:

record1.Field =  record1Value
record2.Field =  record2Value
record3.Field =  record3NewValue
record4.Field =  record4NewValue

结构体方法重写不像 Java/C++ 那样支持父类调用子类方法

import (
	"fmt"
	"testing"
)

type BaseRecord interface {
	Call() string
}

type RecordA struct {
}

func (record *RecordA) Call() string {
	return record.DoCall()
}

func (record *RecordA) DoCall() string {
	return "call RecordA.DoCall()"
}

type RecordB struct {
	RecordA
}

func (record *RecordB) DoCall() string {
	return "call RecordB.DoCall()"
}

func Test(t *testing.T) {
	var record BaseRecord = &RecordB{}

	fmt.Println(record.Call())
}

输出结果:

call RecordA.DoCall()