Golang基础语法梳理(三)有哪些关键点需要掌握?

2026-05-27 11:571阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计3734个文字,预计阅读时间需要15分钟。

Golang基础语法梳理(三)有哪些关键点需要掌握?

好的,以下是对您提供内容的简化

大家好,今天将整理并分享出即将发布的Go语言基础语法内容,供大家学习。请多多指教,谢谢!本次Go语言基础语法内容分为三个章节,本节为第三章节内容。

第三章节:Go语言基础语法

(此处将展开详细讲解Go语言的基础语法内容,包括变量、数据类型、运算符、控制结构等。)

大家好,今天将梳理出的 Go语言基础语法内容,分享给大家。 请多多指教,谢谢。 本次《Go语言基础语法内容》共分为三个章节,本文为第三章节。

大家好,今天将梳理出的 Go语言基础语法内容,分享给大家。 请多多指教,谢谢。

本次《Go语言基础语法内容》共分为三个章节,本文为第三章节

  • Golang 基础之基础语法梳理 (一)
  • Golang 基础之基础语法梳理 (二)
  • Golang 基础之基础语法梳理 (三)
本章节内容
  • interface
  • 反射
  • 泛型
interface 介绍

在Go语言中接口 (interface) 是一种类型, 一种抽象的类型。

接口 (interface) 定义了一个对象的行为规范, 只定义规范不实现,由具体的对象来实现规范的细节。

接口做的事情就像是定义一个协议(规则)。

Interface 是一组method的集合, 是duck-type programming 的一种体现。

接口的定义

  • 接口是一个或多个方法签名的集合
  • 接口只有方法声明,没有实现,没有数据字段
  • 接口可以匿名嵌入其他接口,或嵌入到结构中
  • 接口调用不会做receiver的自动转换
  • 接口同样支持匿名字段方法
  • 接口也可实现类似OOP中的多态
  • 任何类型的方法集中只要拥有该接口'对应的全部方法'签名
  • 只有当接口存储的类型和对象都为nil时,接口才等于nil
  • 用 interface{} 传递任意类型数据是Go语言的惯例用法,而且 interface{} 是类型安全的
  • 空接口可以作为任何类型数据的容器
  • 一个类型可实现多个接口
  • 接口命名习惯以 er 结尾
使用

每个接口由数个方法组成,接口的定义如下

type 接口类型 interface { 方法名1 (参数列表1) 返回值列表1 方法名2 (参数列表2) 返回值列表2 ... }

注意

  1. 接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。

  2. 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。

  3. 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。

例子

type writer interface { Write([]byte) error }

值接收者和指针接收接口

type Mover interface { move() } type dog struct {} func (d dog) move() { fmt.Println("狗狗") } func main() { var x Mover var wangcai = dog{} x = wangcai // x 可以接收dog类型 var fugui = &dog{} // fugui是 *dog 类型 x = fugui // x可以接收*dog类型 指针接收 x.move() }

多个类型实现同一接口

// Mover 接口 type Mover interface { move() } type dog struct { name string } type car struct { brand string } // dog 类型实现 Mover 接口 func (d dog) move() { fmt.Printf("%s: mmmm", d.name) } // car 类型实现 Mover 接口 func (c car) move() { fmt.Printf("%s: mmmm", c.brand) } func main() { var x Mover var a = dog{name: "旺财"} var b = car{brand: "虾米"} x = a x.move() x = b x.move() }

一个接口的方法,不一定需要由一个类型完全实现,接口的方法可以通过在类型中嵌入其他类型或者结构体来实现。

接口嵌套

接口与接口间可以通过嵌套创造出新的接口。

type Sayer interface { say() } type Mover interface { move() } // 接口嵌套 type animal interface { Sayer Mover } // 嵌套得到的接口的使用与普通接口一样 type cat struct { name string } func (c cat) say() { fmt.Println("ssss") } func (c cat) move() { fmt.Println("mmmm") } func main() { var x animal x = cat{name: "花花"} x.move() x.say() }

空接口

空接口是指没有定义任何方法的接口,因此任何类型都实现了空接口。

空接口类型的变量可以存储任意类型的变量。

func main() { // 定义一个空接口 x var x interface{} s := "test data" x = s fmt.Printf("type:%T value: %v\n", x, x) i := 100 x = i fmt.Printf("type:%T value: %v\n", x, x) b := true x = b fmt.Printf("type:%T value: %v\n", x, x) }

空接口作为函数的参数

使用空接口实现可以接收任意类型的函数对象。

func show(a interface{}){ fmt.Printf("type:%T value: %v\n", a, a) }

空接口作为map的参数

使用空接口实现可以保存任意值的字典

var Info = make(map[string]interface{}) Info["id"] = 1 Info["name"] = "帽儿山的枪手" fmt.Println(Info)

获取空接口值

判断空接口中值,可以使用类型断言,语法如下

x.(T)

x 表示类型为 interface{} 的变量

T 表示断言 x 可能是的类型

该语法返回两个参数,第一个参数是 x 转化为 T 类型后的变量, 第二个值是一个布尔值, 若为 true 则表示断言成功, false 则表示失败。

func main() { var x interface{} x = "data" v, ok := x.(string) if ok { fmt.Println(v) } else { fmt.Println("类型断言失败") } }

如果要断言多次,可以写 if 判断, 也可以用 switch 语句实现。

反射 介绍

什么是反射?

例如:有时候我们需要知道某个值是什么类型,才能用对等逻辑去处理它。

以下是常用的处理方法:

// 伪代码 switch value := value.(type){ case string: // 处理操作 case int: // 处理操作 ... }

这样处理,会写的非常长,而且还可能存在自定的类型,也就是说这个判断日后可能还要一直改,因为无法知道未知值到底属于什么类型。

如果使用反射来处理,使用标准库 reflect 中的 TypeOf 和 ValueOf 函数从接口中获取目标对象的信息,就可以轻松处理这个问题。

Golang基础语法梳理(三)有哪些关键点需要掌握?

更多介绍,可参考reflect 官方地址

pkg.go.dev/reflect

Go语言提供了一种机制,在编译时不知道类型的情况下,可更新变量、运行时查看值调用方法以及直接对他们的布局进行操作的机制,称为反射。

使用

使用反射查看对象类型

package main import ( "fmt" "reflect" ) func main() { var name string = "帽儿山的枪手" nameType := reflect.TypeOf(name) nameValue := reflect.ValueOf(name) fmt.Println("name type: ", nameType) fmt.Println("name value: ", nameValue) }

输出

name type: string name value: 帽儿山的枪手

struct 类型反射用法

package main import ( "fmt" "reflect" ) type Info struct { Name string Desc string } func (i Info) Detail() { fmt.Println("detail info") } func main() { i := Info{Name: "帽儿山的枪手", Desc: "技术分享"} t := reflect.TypeOf(i) // 获取目标对象 v := reflect.ValueOf(i) // 获取value值 for i := 0; i < v.NumField(); i++ { // NumField()获取字段总数 key := t.Field(i) // 根据下标,获取包含的key value := v.Field(i).Interface() // 获取key对应的值 fmt.Printf("key=%s value=%v type=%v\n", key.Name, value, key.Type) } // 获取Info的方法 for i := 0; i < t.NumMethod(); i++ { m := t.Method(i) fmt.Printf("方法 Name=%s Type=%v\n", m.Name, m.Type) } }

输出

key=Name value=帽儿山的枪手 type=string key=Desc value=技术分享 type=string 方法 Name=Detail Type=func(main.Info)

通过反射判断类型用法

package main import ( "fmt" "reflect" ) type Info struct { Name string Desc string } func main() { i := Info{Name: "帽儿山的枪手", Desc: "技术分享"} t := reflect.TypeOf(i) // Kind()函数判断值的类型 if k := t.Kind(); k == reflect.Struct { fmt.Println("struct type") } num := 100 switch v := reflect.ValueOf(num); v.Kind() { case reflect.String: fmt.Println("string type") case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: fmt.Println("int type") default: fmt.Printf("unhandled kind %s", v.Kind()) } }

输出

struct type int type

通过反射修改内容

package main import ( "fmt" "reflect" ) type Info struct { Name string Desc string } func main() { i := &Info{Name: "帽儿山的枪手", Desc: "技术分享"} v := reflect.ValueOf(i) // 修改值必须是指针类型 if v.Kind() != reflect.Ptr { fmt.Println("不是指针类型") return } v = v.Elem() // 获取指针指向的元素 name := v.FieldByName("Desc") // 获取目标key的值 name.SetString("好好工作") fmt.Printf("修改后数据: %v\n", *i) }

输出

修改后数据: {帽儿山的枪手 好好工作}

通过反射调用方法

package main import ( "fmt" "reflect" ) type Info struct { Name string Desc string } func (i Info) Detail() { fmt.Println("detail info") } func main() { i := Info{Name: "帽儿山的枪手", Desc: "技术分享"} v := reflect.ValueOf(i) // 获取方法控制权 mv := v.MethodByName("Detail") mv.Call([]reflect.Value{}) // 这里是无调用参数 []reflect.Value{} }

输出

detail info 泛型 介绍

泛型的概念,可以从多态看起,多态是同一形式表现出不同行为的一种特性,在编程语言中被分为两类,临时性多态和参数化多态。

根据实参生成不同的版本,支持任意数量的调用,即泛型,简言之,就是把元素类型变成了参数。

golang版本需要在 1.17版本或以上,才支持泛型使用。

(1.17版本泛型是golang推出的尝鲜版,1.18是正式版本)

举例

func Add(a, b int) int{} func AddFloat(a, b float64) float64{}

在泛型的帮助下,上面代码就可以简化成为:

func Add[T any](a, b T) T

Add后面的[T any],T表示类型的标识,any表示T可以是任意类型。

a、b和返回值的类型T和前面的T是同一个类型。

为什么用[],而不是其他语言中的<>,官方有过解释,大概就是<>会有歧义。曾经计划使用() ,因为太容易混淆,最后使用了[]。

泛型3大概念

  • 类型参数
  • 类型约束
  • 类型推导

特性

  • 函数可以通过type关键字引入额外的类型参数(type parameters)列表:func F(type T)(p T) { ... }
  • 这些类型参数可以像一般的参数一样在函数体中使用
  • 类型也可以拥有类型参数列表:type M(type T) []T
  • 每个类型参数可以拥有一个约束:func F(type T Constraint)(p T) { ... }
  • 使用interface来描述类型的约束
  • 被用作类型约束的interface可以拥有一个预声明类型列表,限制了实现此接口的类型的基础类型
  • 使用泛型函数或类型时需要传入类型实参
  • 类型推断允许用户在调用泛型函数时省略类型实参
  • 泛型函数只允许进行类型约束所规定的操作
使用

对泛型进行输出

如果Go当前版本是1.17版本,运行时需要加参数 -gcflags=-G=3

# 完整命令 go run -gcflags=-G=3 example.go

示例

package main import ( "fmt" ) func print[T any](s []T) { for _, v := range s { fmt.Printf("%v ", v) } fmt.Printf("\n") } func main() { print[int]([]int{1,2,3,4}) print[float64]([]float64{1.01, 2.02, 3.03, 4.04}) print[string]([]string{"a", "b", "c", "d"}) }

输出

1 2 3 4 1.01 2.02 3.03 4.04 a b c d

Go1.18 中,any 是 interface{} 的别名

使用泛型约束,控制类型的使用范围

原先的语法中,类型约束会用逗号分隔的方式来展示

type int, int8, int16, int32, int64

在新语法中,结合定义为 union element(联合元素),写成一系列由竖线 ”|“ 分隔的类型或近似元素。

int | int8 | int16 | int32 | int64

示例

package main import ( "fmt" ) type CustomType interface { int | int8 | int16 | int32 | int64 | string } func add[T CustomType] (a, b T) T{ return a + b } func main() { fmt.Println(add(1, 2)) fmt.Println(add("帽儿山的枪手", "技术分享")) }

输出

3 帽儿山的枪手技术分享

上述 CustomType 接口类型也可以写成以下格式

type CustomType interface { ~int | ~string }

上述声明的类型集是 ~int,也就是所有类型为 int 的类型(如:int、int8、int16、int32、int64)都能够满足这个类型约束的条件。

泛型中自带 comparable 约束

因为不是所有的类型都可以==比较,所以Golang内置提供了一个comparable约束,表示可比较的。

官方说明

comparable是由所有可比较类型(布尔、数字、字符串、指针、通道、可比较类型的数组、字段均为可比较类型的结构)实现的接口。可比较接口只能用作类型参数约束,不能用作变量的类型。

pkg.go.dev/builtin@master#comparable

package main import ( "fmt" ) func diff[T comparable](a []T, v T) { for _, e := range a { if e == v { fmt.Println(e) } } } func main() { diff([]int{1, 2, 3, 4}, 3) }

输出

3

泛型中操作指针

package main import ( "fmt" ) func pointerOf[T any](v T) *T { return &v } func main() { name := pointerOf("帽儿山的枪手") fmt.Println(*name) id := pointerOf(100) fmt.Println(*id) }

输出

帽儿山的枪手 100

技术文章持续更新,请大家多多关注呀~~

搜索微信公众号,关注我

搜索微信公众号,关注我

本文共计3734个文字,预计阅读时间需要15分钟。

Golang基础语法梳理(三)有哪些关键点需要掌握?

好的,以下是对您提供内容的简化

大家好,今天将整理并分享出即将发布的Go语言基础语法内容,供大家学习。请多多指教,谢谢!本次Go语言基础语法内容分为三个章节,本节为第三章节内容。

第三章节:Go语言基础语法

(此处将展开详细讲解Go语言的基础语法内容,包括变量、数据类型、运算符、控制结构等。)

大家好,今天将梳理出的 Go语言基础语法内容,分享给大家。 请多多指教,谢谢。 本次《Go语言基础语法内容》共分为三个章节,本文为第三章节。

大家好,今天将梳理出的 Go语言基础语法内容,分享给大家。 请多多指教,谢谢。

本次《Go语言基础语法内容》共分为三个章节,本文为第三章节

  • Golang 基础之基础语法梳理 (一)
  • Golang 基础之基础语法梳理 (二)
  • Golang 基础之基础语法梳理 (三)
本章节内容
  • interface
  • 反射
  • 泛型
interface 介绍

在Go语言中接口 (interface) 是一种类型, 一种抽象的类型。

接口 (interface) 定义了一个对象的行为规范, 只定义规范不实现,由具体的对象来实现规范的细节。

接口做的事情就像是定义一个协议(规则)。

Interface 是一组method的集合, 是duck-type programming 的一种体现。

接口的定义

  • 接口是一个或多个方法签名的集合
  • 接口只有方法声明,没有实现,没有数据字段
  • 接口可以匿名嵌入其他接口,或嵌入到结构中
  • 接口调用不会做receiver的自动转换
  • 接口同样支持匿名字段方法
  • 接口也可实现类似OOP中的多态
  • 任何类型的方法集中只要拥有该接口'对应的全部方法'签名
  • 只有当接口存储的类型和对象都为nil时,接口才等于nil
  • 用 interface{} 传递任意类型数据是Go语言的惯例用法,而且 interface{} 是类型安全的
  • 空接口可以作为任何类型数据的容器
  • 一个类型可实现多个接口
  • 接口命名习惯以 er 结尾
使用

每个接口由数个方法组成,接口的定义如下

type 接口类型 interface { 方法名1 (参数列表1) 返回值列表1 方法名2 (参数列表2) 返回值列表2 ... }

注意

  1. 接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。

  2. 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。

  3. 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。

例子

type writer interface { Write([]byte) error }

值接收者和指针接收接口

type Mover interface { move() } type dog struct {} func (d dog) move() { fmt.Println("狗狗") } func main() { var x Mover var wangcai = dog{} x = wangcai // x 可以接收dog类型 var fugui = &dog{} // fugui是 *dog 类型 x = fugui // x可以接收*dog类型 指针接收 x.move() }

多个类型实现同一接口

// Mover 接口 type Mover interface { move() } type dog struct { name string } type car struct { brand string } // dog 类型实现 Mover 接口 func (d dog) move() { fmt.Printf("%s: mmmm", d.name) } // car 类型实现 Mover 接口 func (c car) move() { fmt.Printf("%s: mmmm", c.brand) } func main() { var x Mover var a = dog{name: "旺财"} var b = car{brand: "虾米"} x = a x.move() x = b x.move() }

一个接口的方法,不一定需要由一个类型完全实现,接口的方法可以通过在类型中嵌入其他类型或者结构体来实现。

接口嵌套

接口与接口间可以通过嵌套创造出新的接口。

type Sayer interface { say() } type Mover interface { move() } // 接口嵌套 type animal interface { Sayer Mover } // 嵌套得到的接口的使用与普通接口一样 type cat struct { name string } func (c cat) say() { fmt.Println("ssss") } func (c cat) move() { fmt.Println("mmmm") } func main() { var x animal x = cat{name: "花花"} x.move() x.say() }

空接口

空接口是指没有定义任何方法的接口,因此任何类型都实现了空接口。

空接口类型的变量可以存储任意类型的变量。

func main() { // 定义一个空接口 x var x interface{} s := "test data" x = s fmt.Printf("type:%T value: %v\n", x, x) i := 100 x = i fmt.Printf("type:%T value: %v\n", x, x) b := true x = b fmt.Printf("type:%T value: %v\n", x, x) }

空接口作为函数的参数

使用空接口实现可以接收任意类型的函数对象。

func show(a interface{}){ fmt.Printf("type:%T value: %v\n", a, a) }

空接口作为map的参数

使用空接口实现可以保存任意值的字典

var Info = make(map[string]interface{}) Info["id"] = 1 Info["name"] = "帽儿山的枪手" fmt.Println(Info)

获取空接口值

判断空接口中值,可以使用类型断言,语法如下

x.(T)

x 表示类型为 interface{} 的变量

T 表示断言 x 可能是的类型

该语法返回两个参数,第一个参数是 x 转化为 T 类型后的变量, 第二个值是一个布尔值, 若为 true 则表示断言成功, false 则表示失败。

func main() { var x interface{} x = "data" v, ok := x.(string) if ok { fmt.Println(v) } else { fmt.Println("类型断言失败") } }

如果要断言多次,可以写 if 判断, 也可以用 switch 语句实现。

反射 介绍

什么是反射?

例如:有时候我们需要知道某个值是什么类型,才能用对等逻辑去处理它。

以下是常用的处理方法:

// 伪代码 switch value := value.(type){ case string: // 处理操作 case int: // 处理操作 ... }

这样处理,会写的非常长,而且还可能存在自定的类型,也就是说这个判断日后可能还要一直改,因为无法知道未知值到底属于什么类型。

如果使用反射来处理,使用标准库 reflect 中的 TypeOf 和 ValueOf 函数从接口中获取目标对象的信息,就可以轻松处理这个问题。

Golang基础语法梳理(三)有哪些关键点需要掌握?

更多介绍,可参考reflect 官方地址

pkg.go.dev/reflect

Go语言提供了一种机制,在编译时不知道类型的情况下,可更新变量、运行时查看值调用方法以及直接对他们的布局进行操作的机制,称为反射。

使用

使用反射查看对象类型

package main import ( "fmt" "reflect" ) func main() { var name string = "帽儿山的枪手" nameType := reflect.TypeOf(name) nameValue := reflect.ValueOf(name) fmt.Println("name type: ", nameType) fmt.Println("name value: ", nameValue) }

输出

name type: string name value: 帽儿山的枪手

struct 类型反射用法

package main import ( "fmt" "reflect" ) type Info struct { Name string Desc string } func (i Info) Detail() { fmt.Println("detail info") } func main() { i := Info{Name: "帽儿山的枪手", Desc: "技术分享"} t := reflect.TypeOf(i) // 获取目标对象 v := reflect.ValueOf(i) // 获取value值 for i := 0; i < v.NumField(); i++ { // NumField()获取字段总数 key := t.Field(i) // 根据下标,获取包含的key value := v.Field(i).Interface() // 获取key对应的值 fmt.Printf("key=%s value=%v type=%v\n", key.Name, value, key.Type) } // 获取Info的方法 for i := 0; i < t.NumMethod(); i++ { m := t.Method(i) fmt.Printf("方法 Name=%s Type=%v\n", m.Name, m.Type) } }

输出

key=Name value=帽儿山的枪手 type=string key=Desc value=技术分享 type=string 方法 Name=Detail Type=func(main.Info)

通过反射判断类型用法

package main import ( "fmt" "reflect" ) type Info struct { Name string Desc string } func main() { i := Info{Name: "帽儿山的枪手", Desc: "技术分享"} t := reflect.TypeOf(i) // Kind()函数判断值的类型 if k := t.Kind(); k == reflect.Struct { fmt.Println("struct type") } num := 100 switch v := reflect.ValueOf(num); v.Kind() { case reflect.String: fmt.Println("string type") case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: fmt.Println("int type") default: fmt.Printf("unhandled kind %s", v.Kind()) } }

输出

struct type int type

通过反射修改内容

package main import ( "fmt" "reflect" ) type Info struct { Name string Desc string } func main() { i := &Info{Name: "帽儿山的枪手", Desc: "技术分享"} v := reflect.ValueOf(i) // 修改值必须是指针类型 if v.Kind() != reflect.Ptr { fmt.Println("不是指针类型") return } v = v.Elem() // 获取指针指向的元素 name := v.FieldByName("Desc") // 获取目标key的值 name.SetString("好好工作") fmt.Printf("修改后数据: %v\n", *i) }

输出

修改后数据: {帽儿山的枪手 好好工作}

通过反射调用方法

package main import ( "fmt" "reflect" ) type Info struct { Name string Desc string } func (i Info) Detail() { fmt.Println("detail info") } func main() { i := Info{Name: "帽儿山的枪手", Desc: "技术分享"} v := reflect.ValueOf(i) // 获取方法控制权 mv := v.MethodByName("Detail") mv.Call([]reflect.Value{}) // 这里是无调用参数 []reflect.Value{} }

输出

detail info 泛型 介绍

泛型的概念,可以从多态看起,多态是同一形式表现出不同行为的一种特性,在编程语言中被分为两类,临时性多态和参数化多态。

根据实参生成不同的版本,支持任意数量的调用,即泛型,简言之,就是把元素类型变成了参数。

golang版本需要在 1.17版本或以上,才支持泛型使用。

(1.17版本泛型是golang推出的尝鲜版,1.18是正式版本)

举例

func Add(a, b int) int{} func AddFloat(a, b float64) float64{}

在泛型的帮助下,上面代码就可以简化成为:

func Add[T any](a, b T) T

Add后面的[T any],T表示类型的标识,any表示T可以是任意类型。

a、b和返回值的类型T和前面的T是同一个类型。

为什么用[],而不是其他语言中的<>,官方有过解释,大概就是<>会有歧义。曾经计划使用() ,因为太容易混淆,最后使用了[]。

泛型3大概念

  • 类型参数
  • 类型约束
  • 类型推导

特性

  • 函数可以通过type关键字引入额外的类型参数(type parameters)列表:func F(type T)(p T) { ... }
  • 这些类型参数可以像一般的参数一样在函数体中使用
  • 类型也可以拥有类型参数列表:type M(type T) []T
  • 每个类型参数可以拥有一个约束:func F(type T Constraint)(p T) { ... }
  • 使用interface来描述类型的约束
  • 被用作类型约束的interface可以拥有一个预声明类型列表,限制了实现此接口的类型的基础类型
  • 使用泛型函数或类型时需要传入类型实参
  • 类型推断允许用户在调用泛型函数时省略类型实参
  • 泛型函数只允许进行类型约束所规定的操作
使用

对泛型进行输出

如果Go当前版本是1.17版本,运行时需要加参数 -gcflags=-G=3

# 完整命令 go run -gcflags=-G=3 example.go

示例

package main import ( "fmt" ) func print[T any](s []T) { for _, v := range s { fmt.Printf("%v ", v) } fmt.Printf("\n") } func main() { print[int]([]int{1,2,3,4}) print[float64]([]float64{1.01, 2.02, 3.03, 4.04}) print[string]([]string{"a", "b", "c", "d"}) }

输出

1 2 3 4 1.01 2.02 3.03 4.04 a b c d

Go1.18 中,any 是 interface{} 的别名

使用泛型约束,控制类型的使用范围

原先的语法中,类型约束会用逗号分隔的方式来展示

type int, int8, int16, int32, int64

在新语法中,结合定义为 union element(联合元素),写成一系列由竖线 ”|“ 分隔的类型或近似元素。

int | int8 | int16 | int32 | int64

示例

package main import ( "fmt" ) type CustomType interface { int | int8 | int16 | int32 | int64 | string } func add[T CustomType] (a, b T) T{ return a + b } func main() { fmt.Println(add(1, 2)) fmt.Println(add("帽儿山的枪手", "技术分享")) }

输出

3 帽儿山的枪手技术分享

上述 CustomType 接口类型也可以写成以下格式

type CustomType interface { ~int | ~string }

上述声明的类型集是 ~int,也就是所有类型为 int 的类型(如:int、int8、int16、int32、int64)都能够满足这个类型约束的条件。

泛型中自带 comparable 约束

因为不是所有的类型都可以==比较,所以Golang内置提供了一个comparable约束,表示可比较的。

官方说明

comparable是由所有可比较类型(布尔、数字、字符串、指针、通道、可比较类型的数组、字段均为可比较类型的结构)实现的接口。可比较接口只能用作类型参数约束,不能用作变量的类型。

pkg.go.dev/builtin@master#comparable

package main import ( "fmt" ) func diff[T comparable](a []T, v T) { for _, e := range a { if e == v { fmt.Println(e) } } } func main() { diff([]int{1, 2, 3, 4}, 3) }

输出

3

泛型中操作指针

package main import ( "fmt" ) func pointerOf[T any](v T) *T { return &v } func main() { name := pointerOf("帽儿山的枪手") fmt.Println(*name) id := pointerOf(100) fmt.Println(*id) }

输出

帽儿山的枪手 100

技术文章持续更新,请大家多多关注呀~~

搜索微信公众号,关注我

搜索微信公众号,关注我