Golang基础-10

Go语言基础

介绍

基础

介绍

  • 本文介绍Go语言中方法定义、方法调用、String方法、结构体嵌套方法调用、方法初始化变量、接口定义、接口声明、接口变量赋值、接口的嵌套、类型断言、匿名接口与空接口等相关知识。

基础

方法
  • 方法是对类型定义的,且只能由该类型调用的函数。
  • 通过 type 自定义类型(结构体或任何自定义的类型),可对此类型定义方法。* 方法具有特定接收者(附加的类型),接收者可以是指针或值类型。
  • 函数不属于任何类型,方法指定特定的接收者类型。
// 方法的一般格式
func (t *T或T) MethodName(参数列表) (返回值列表) {
    // 方法体
}
方法定义
  • 方法必须是自定义类型,Go语言内置类型不支持用户对此类型拓展方法,使用 type 对内置类型自定义别名,自定义后的别名可以定义方法。
  • 方法声明要与接收者参数的基类型声明放在同一个包内。
type Cpu struct {
	Name string
	Id   int
}

// 接收者为值类型
func (c Cpu) GetName() string {
	return c.Name
}

func (c Cpu) GetId() int {
	return c.Id
}

// 接收者为指针类型
func (c *Cpu) PGetName() string {
	return c.Name
}

func (c *Cpu) PGetId() int {
	return c.Id
}
方法调用
  • 定义类型对象,调用类型方法时使用其对象调用。
  • 指针类型对象,调用接收者为值类型方法时,Go 编译器会自动将指针对象解引用为值调用方法。
  • 值类型对象,调用接收者为指针类型方法时,Go 编译器会自动将值对象取引用为指针调用方法。
  • 实际使用时若存在指针接收者,则所有方法使用指针接收者,否则都是用值接收者。
  • 接收者为指针类型的方法,运行时必须确保接收者不为 nil。
package main

import "fmt"

type Cpu struct {
	Name string
	Id   int
}

func (c Cpu) GetName() string {
	return c.Name
}

func (c Cpu) GetId() int {
	return c.Id
}

func (c *Cpu) PGetName() string {
	return c.Name
}

func (c *Cpu) PGetId() int {
	return c.Id
}

func main() {
	// 定义结构体对象
	cpu := Cpu{
		"Intel",
		10001,
	}
	fmt.Print("Name = ", (&cpu).PGetName()) // 常用写法 cpu.PPGetName()
	fmt.Println(", Id = ", cpu.GetId())

	// 定义结构体对象指针
	cpu2 := &Cpu{
		"AMD",
		10002,
	}

	// 两种方式均可调用
	fmt.Print("Name = ", (*cpu2).GetName())
	fmt.Println(", Id = ", cpu2.PGetId())
}

输出结果
Name = Intel, Id = 10001
Name = AMD, Id = 10002

String方法
  • 对于自定义结构体类型,通过实现 String 方法,就能调用 fmt.Println 函数输出结构体信息。
package main

import "fmt"

type Cpu struct {
	Name string
	Id   int
}

func main() {
	// 定义结构体对象
	cpu := Cpu{
		"Intel",
		10001,
	}
	fmt.Print(&cpu)
}

func (c *Cpu) String() string {
	ret := fmt.Sprintf("Name = %v, Age = %v", c.Name, c.Id)
	return ret
}

输出结果
Name = Intel, Age = 10001

结构体嵌套方法调用
  • 结构体嵌套有两种方式,匿名嵌套与命名嵌套。
  • 命名嵌套时,通过嵌套结构体的对象可以调用嵌套结构体的方法。
  • 匿名嵌套时,通过被嵌套结构体的对象可直接调用嵌套结构体的方法,若被嵌套结构体和嵌套结构体之间有同名方法,则无法调用到匿名嵌套结构体中的函数,此时就需要指定命名嵌套方式。
package main

import "fmt"

type Base struct {
	name string
}

func (b Base) GetName() string {
	return b.name
}

type Intel struct {
	base Base
	id   int
}

func (i Intel) GetName() string {
	ret := fmt.Sprintf("name = %v, id = %v", i.base.GetName(), i.id)
	return ret
}

type Amd struct {
	Base
	id int
}

// 定义此方法,编译通过,运行时报错
// func (a Amd) GetName() string {
// 	ret := fmt.Sprintf("name = %v, id = %v", a.GetName(), a.id)
// 	return ret
// }

func main() {
	// 定义结构体对象
	a := Intel{
		Base{"Intel"},
		10001,
	}
	fmt.Println(a)
	fmt.Println("a.GetName: ", a.GetName())
	fmt.Println("a.base.GetName: ", a.base.GetName())

	b := Amd{
		Base{"AMD"},
		10002,
	}
	fmt.Println(b)
	fmt.Println("b.GetName: ", b.GetName())
}

输出结果
{{Intel} 10001}
a.GetName: name = Intel, id = 10001
a.base.GetName: Intel
{{AMD} 10002}
b.GetName: AMD

方法初始化变量
  • 将方法可以作为类型初始化变量,然后使用此变量调用方法,可以存储,也可以作为形参传递或作为函数返回值。
  • 方法表达式赋值时,若方法接收者为值类型,赋值时拷贝值类型,若方法接收者为指针类型,会自动解引用拷贝。
package main

import "fmt"

type Base struct {
	name string
}

func (b Base) GetName() string {
	return b.name
}

func (b *Base) SetName(nm string) {
	b.name = nm
}

func Test(f func() string) {
	fmt.Println(f())
}

func Test2(str string, f func(string)) {
	f(str)
}

func main() {
	// 定义结构体对象
	a := Base{
		"Intel",
	}
	// 定义结构体对象,调用接收者为值的方法
	aa := a.GetName
	fmt.Printf("aa type: %T, value: %v\n", aa, aa())

	// 定义结构体对象,调用接收者为指针的方法
	ab := a.SetName
	fmt.Printf("ab type: %T", ab)
	ab("AMD")
	fmt.Printf(", value: %v\n", a.GetName())

	// 方法作为形参值传递
	Test(a.GetName)
	Test2("Intel+", a.SetName)
	Test(a.GetName)
}

输出结果
aa type: func() string, value: Intel
ab type: func(string), value: AMD
AMD
Intel+


接口
  • Go语言使用组合实现面向对象的概念。接口是自定义类型,是对其他类型行为的抽象。将不同的类型绑定到一组公共的方法上,实现多态和灵活的设计。
  • Go语言的接口设计是非侵入式的,接口定义者不需要关注什么类型实现此接口,接口实现者只需要实现对应接口,通过编译器编译时自动获取到某类型使用某接口。
  • Go 语言中的接口是隐式实现的,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。可以通过接口实现多态。
  • 可以存在空接口,Go语言中, 类型interface{}(any)每个类型都默认会实现,所以此类型可转换为任意类型。
type 接口类型名 interface {
    方法名1(参数列表1) 返回值列表1
    方法名2(参数列表2) 返回值列表2
    ...
}
接口定义
  • 使用 interface 关键字定义,声明了一系列的函数签名(函数名、函数参数、函数返回值)。
type GameRole interface {
	Attack(att int) error
	Cure(cur int) error
	Visit(msg string) error
}
接口声明
  • 将变量名类型定义为接口名称,变量默认初始化为 nil。
package main

import "fmt"

type GameRole interface {
	Attack(att int) error
	Cure(cur int) error
	Visit(msg string) error
}

func main() {
	var a GameRole
	fmt.Printf("a type: %T, value: %v\n", a, a)
}

输出结果
a type: , value:

接口变量赋值
  • 接口变量赋值前提是需要实现接口,且接口的方法与实现接口的类型方法格式一样,而且接口中声明的所有函数均需要被实现。

  • 当自定义类型实现了接口类型中声明的所有函数时,该类型的对象可以赋值给接口变量。

  • 方法接收者都为值类型。

package main

import "fmt"

// 定义接口
type GameRole interface {
	Attack(att int) error
	Cure(cur int) error
	Visit(msg string) error
}

// 定义结构体类型
type Warcraft struct {
	name    string
	lifeBar int
}

// 实现所有接口方法
func (c Warcraft) Attack(att int) error {
	fmt.Println(c.name, ", ", c.lifeBar, ", Attack: ", att)
	return nil
}
func (c Warcraft) Cure(cur int) error {
	fmt.Println(c.name, ", ", c.lifeBar, ", Cure: ", cur)
	return nil
}
func (c Warcraft) Visit(msg string) error {
	fmt.Println(c.name, ", ", c.lifeBar, ", Visit: ", msg)
	return nil
}

func main() {
	var a GameRole
	fmt.Printf("a type: %T, value: %v\n", a, a)

    // 接收者都为值类型
	var b GameRole = Warcraft{"Warcraft-2", 200}
	fmt.Printf("b type: %T, value: %v\n", b, b)

    // 方法调用
	b.Attack(10)
	b.Cure(5)
	b.Visit("World")
}

a type: , value:
b type: main.Warcraft, value: {Warcraft-2 200}
Warcraft-2 , 200 , Attack: 10
Warcraft-2 , 200 , Cure: 5
Warcraft-2 , 200 , Visit: World

  • 方法接收者都为指针类型。
package main

import "fmt"

type GameRole interface {
	Attack(att int) error
	Cure(cur int) error
	Visit(msg string) error
}

type Warcraft struct {
	name    string
	lifeBar int
}

func (c *Warcraft) Attack(att int) error {
	fmt.Println(c.name, ", ", c.lifeBar, ", Attack: ", att)
	return nil
}
func (c *Warcraft) Cure(cur int) error {
	fmt.Println(c.name, ", ", c.lifeBar, ", Cure: ", cur)
	return nil
}
func (c *Warcraft) Visit(msg string) error {
	fmt.Println(c.name, ", ", c.lifeBar, ", Visit: ", msg)
	return nil
}

func main() {
	var a GameRole
	fmt.Printf("a type: %T, value: %v\n", a, a)

	var b GameRole = &Warcraft{"Warcraft-2", 200}
	fmt.Printf("b type: %T, value: %v\n", b, b)

	b.Attack(10)
	b.Cure(5)
	b.Visit("World")
}

输出结果
a type: , value:
b type: *main.Warcraft, value: &{Warcraft-2 200}
Warcraft-2 , 200 , Attack: 10
Warcraft-2 , 200 , Cure: 5
Warcraft-2 , 200 , Visit: World

  • 方法接收者即有值又有指针类型,接口变量赋值需要使用指针赋值方式。
  • 一个类型可用同时实现多个接口,接口间彼此独立。
  • 只要某个类型实现一个或多个接口声明的所有函数,就可以赋值给各自的接口类型。
package main

import "fmt"

// 定义接口类型
type GameRole interface {
	Attack(att int) error
	Cure(cur int) error
	Visit(msg string) error
}

// 定义接口类型
type GameExit interface {
	ExitGame() error
}

// 定义结构体
type Warcraft struct {
	name    string
	lifeBar int
}

// 实现接口
func (c Warcraft) Attack(att int) error {
	fmt.Println(c.name, ", ", c.lifeBar, ", Attack: ", att)
	return nil
}
func (c Warcraft) Cure(cur int) error {
	fmt.Println(c.name, ", ", c.lifeBar, ", Cure: ", cur)
	return nil
}
func (c *Warcraft) Visit(msg string) error {
	fmt.Println(c.name, ", ", c.lifeBar, ", Visit: ", msg)
	return nil
}

func (c Warcraft) ExitGame() error {
	fmt.Println(c.name, ", ", c.lifeBar, ", ExitGame")
	return nil
}

func main() {
	var a GameRole
	fmt.Printf("a type: %T, value: %v\n", a, a)

	var b GameRole = &Warcraft{"Warcraft-2", 200}
	fmt.Printf("b type: %T, value: %v\n", b, b)

	b.Attack(10)
	b.Cure(5)
	b.Visit("World")

	var c GameExit = Warcraft{"Warcraft-3", 200}
	c.ExitGame()
}

输出结果
a type: , value:
b type: *main.Warcraft, value: &{Warcraft-2 200}
Warcraft-2 , 200 , Attack: 10
Warcraft-2 , 200 , Cure: 5
Warcraft-2 , 200 , Visit: World
Warcraft-3 , 200 , ExitGame

  • 当接口之间存在子集关系(一个接口(A)包含另一个接口(B)中的所有声明函数)时,A接口变量可以赋值给B接口变量。若两个接口声明同样的函数签名,两个接口完全等价。
  • 接口变量声明为什么类型就调用此类型的方法。
package main

import "fmt"

// 定义接口类型
type ExitALL interface {
	GameExit() error
	ViewExit() error
}

// 定义接口类型
type GameExit interface {
	GameExit() error
}

// 定义接口类型
type CarGameExit interface {
	GameExit() error
}

// 定义结构体
type AppExit struct {
	name string
}

// 实现接口
func (c AppExit) GameExit() error {
	fmt.Println(c.name, " game exit now")
	return nil
}

func (c AppExit) ViewExit() error {
	fmt.Println(c.name, " view exit now")
	return nil
}

func main() {
	var a ExitALL = AppExit{"TV"}
	fmt.Printf("a type: %T, value: %v\n", a, a)
	var b GameExit = AppExit{"SV"}
	fmt.Printf("b type: %T, value: %v\n", b, b)

	// GameExit 接口为 ExitALL 接口的子集
	var c GameExit = a
	fmt.Printf("c type: %T, value: %v\n", c, c)

	// GameExit 和 CarGameExit 具有相同的接口,两者完全等价
	var d CarGameExit = b
	fmt.Printf("d type: %T, value: %v\n", d, d)

	a.GameExit()
	b.GameExit()
	c.GameExit()
	d.GameExit()
}

输出结果
a type: main.AppExit, value: {TV}
b type: main.AppExit, value: {SV}
c type: main.AppExit, value: {TV}
d type: main.AppExit, value: {SV}
TV game exit now
SV game exit now
TV game exit now
SV game exit now

接口的嵌套
  • 接口间可以嵌套组合,将多个接口放到一个接口中。
package main

import "fmt"

// 定义接口类型
type FileRead interface {
	Read(read *[]byte) (int, error)
}

type FileWrite interface {
	Write(msg string) (int, error)
}

type FileClose interface {
	Close() error
}

type FileOpen interface {
	Open() error
}

// 定义接口嵌套
type FileOpt interface {
	FileRead
	FileWrite
	FileOpen
	FileClose
}

// 定义结构体
type File struct {
	path   string
	handle *int
}

// 实现接口
func (c *File) Read(read *[]byte) (int, error) {
	if c.handle == nil || len(*read) < 1 {
		return -1, fmt.Errorf("handle == nil")
	}

	var lens int = 0
	arr := [...]byte{'1', '2', '3', '4'}
	if len(arr) > len(*read) {
		lens = len(*read)
	} else {
		lens = len(arr)
	}

	fmt.Println("file handle: ", c.handle, " read byte size: ", lens)
	*read = arr[:lens]
	return lens, nil
}
func (c *File) Write(msg string) (int, error) {
	if c.handle == nil {
		return -1, fmt.Errorf("handle == nil")
	}
	fmt.Println("file handle: ", c.handle, " wirte msg: ", msg)
	return len(msg), nil
}
func (c *File) Open() error {
	c.handle = new(int)
	*c.handle = 1
	fmt.Println("Open ", c.path, " succeed")
	return nil
}
func (c *File) Close() error {
	if c.handle == nil {
		return nil
	}

	c.handle = nil
	fmt.Println("Close ", c.path, " succeed")
	return nil
}

func main() {
	// 声明接口变量
	var a FileOpt = &File{"./AppFile.txt", nil}
	fmt.Printf("a type: %T, value: %v\n", a, a)

	// 调用Open方法
	if err := a.Open(); err != nil {
		fmt.Println("Open file failed")
		return
	}

	// 延迟调用Close方法
	defer a.Close()

	var lens int = 0

	// 调用Write方法
	lens, _ = a.Write("WriteData")
	fmt.Println("write data len: ", lens)

	b := []byte{10, 20, 30, 40}
	// 调用Read方法
	lens, _ = a.Read(&b)
	fmt.Printf("b type: %T, value: %v, read data len: %v\n", b, b, lens)
}

输出结果
a type: *main.File, value: &{./AppFile.txt }
Open ./AppFile.txt succeed
file handle: 0xc00000a0d0 wirte msg: WriteData
write data len: 9
file handle: 0xc00000a0d0 read byte size: 4
b type: []uint8, value: [49 50 51 52], read data len: 4
Close ./AppFile.txt succeed

类型断言
  • 接口变量赋值时使用类型对象或将父集接口赋值给子集变量后,如果需要将接口变量重新还原为原来的类型,使用类型断言。语法:接口变量.(Type)。
package main

import "fmt"

// 定义接口类型
type ExitALL interface {
	GameExit() error
	ViewExit() error
}

// 定义接口类型
type GameExit interface {
	GameExit() error
}

// 定义接口类型
type CarGameExit interface {
	GameExit() error
}

// 定义结构体
type AppExit struct {
	name string
	id   int
}

// 实现接口
func (c AppExit) GameExit() error {
	fmt.Println(c.name, " game exit now")
	return nil
}

func (c AppExit) ViewExit() error {
	fmt.Println(c.name, " view exit now")
	return nil
}

func main() {
	var a ExitALL = AppExit{"TV", 10}
	fmt.Printf("a type: %T, value: %v\n", a, a)
	var b GameExit = AppExit{"SV", 10}
	fmt.Printf("b type: %T, value: %v\n", b, b)

	// GameExit 接口为 ExitALL 接口的子集
	var c GameExit = a
	fmt.Printf("c type: %T, value: %v\n", c, c)

	m1, ok1 := a.(AppExit)
	fmt.Println("m1: ", m1, ", ok1: ", ok1)

	m2, ok2 := b.(*AppExit)
	fmt.Println("m2: ", m2, ", ok2: ", ok2)

	m3, ok3 := c.(ExitALL)
	fmt.Println("m3: ", m3, ", ok3: ", ok3)
}

输出结果
a type: main.AppExit, value: {TV 10}
b type: main.AppExit, value: {SV 10}
c type: main.AppExit, value: {TV 10}
m1: {TV 10} , ok1: true
m2: , ok2: false
m3: {TV 10} , ok3: true

  • 通过 switch-case 语句查询变量类型,执行对应的操作。语法:接口变量.(type),case分支中不允许写 fallthrough 关键字。
package main

import (
	"fmt"
	"reflect"
)

// 定义接口类型
type ExitALL interface {
	GameExit() error
	ViewExit() error
}

// 定义接口类型
type GameExit interface {
	GameExit() error
}

// 定义接口类型
type CarGameExit interface {
	GameExit() error
}

// 定义结构体
type AppExit struct {
	name string
	id   int
}

// 实现接口
func (c AppExit) GameExit() error {
	fmt.Println(c.name, " game exit now")
	return nil
}

func (c AppExit) ViewExit() error {
	fmt.Println(c.name, " view exit now")
	return nil
}

func main() {
	var a ExitALL = AppExit{"TV", 10}
	fmt.Printf("a type: %T, value: %v\n", a, a)
	var b GameExit = AppExit{"SV", 10}
	fmt.Printf("b type: %T, value: %v\n", b, b)

	// GameExit 接口为 ExitALL 接口的子集
	var c GameExit = a
	fmt.Printf("c type: %T, value: %v\n", c, c)

	var e GameExit
	var d GameExit = e
	fmt.Printf("d type: %T, value: %v\n", d, d)

	// 声明切片
	var s []GameExit = []GameExit{a, b, c, d, e}
	fmt.Println(s)

	for i, v := range s {
		switch v.(type) {
		case AppExit:
			fmt.Printf("index %v, AppExit value: %v\n", i, v)
		case *AppExit:
			fmt.Printf("index %v, *AppExit value: %v\n", i, v)
		case ExitALL:
			fmt.Printf("index %v, ExitALL value: %v\n", i, v)
		case GameExit:
			fmt.Printf("index %v, GameExit value: %v\n", i, v)
		default:
			fmt.Println("index ", i, " is other type: ", reflect.TypeOf(v))
		}
	}
}

输出结果
index 0, AppExit value: {TV 10}
index 1, AppExit value: {SV 10}
index 2, AppExit value: {TV 10}
index 3 is other type:
index 4 is other type:

匿名接口与空接口
  • 将接口变量直接声明为接口的函数签名,称为匿名接口。
  • 匿名接口的使用场景一般是初始化一次接口变量。将实现此接口的类型变量赋值给此匿名接口的变量。
package main

import "fmt"

type Exit struct {
}

func (c *Exit) Close() error {
	fmt.Println("Exit Close")
	return nil
}

func main() {
	// 声明匿名接口变量
	var a interface {
		Close() error
	} = &Exit{}

	// 调用类型方法
	a.Close()
}

输出结果
Exit Close

  • Go语言中,定义 interface{} 称之为空接口,不包含任何函数签名,且空接口声明的变量可以赋值为任何类型的变量。声明函数参数类型为 interface{},用于接收任意类型的变量。在 Go1.18以后,添加了 空接口别名 any。
package main

import "fmt"

func main() {
	var a interface{} = "interface"
	var b interface{} = 10
	var c interface{} = 3.14
	var d any = [...]int{10, 20, 30}
	fmt.Println("a = ", a, ", b = ", b, ", c = ", c, ", d = ", d)
}

输出结果
a = interface , b = 10 , c = 3.14 , d = [10 20 30]

起始

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值