Golang 中文学习文档地址
1、接口
1.1 概念
在 Go 语言中,接口是一种抽象类型,用于定义一组方法签名而不提供方法的实现。接口的核心理念是描述行为,而具体的行为实现由实现接口的类型提供。接口在 Go 语言中广泛用于实现多态性、松耦合和代码复用。
1.2 声明
type Person interface {
Say(string) string
Walk(int)
} #技术分享
1.3 初始化
- 所谓接口,仅仅定义了一组规范,所以 单单接口是无法初始化的,没有具体的实现 。
package main
import "fmt"
type Person interface { Name() string Age() int }
func main() { var p Person fmt.Println(p) }
1.4 接口的实现
- 场景
一家建筑公司,需要一台起重机,然后出了一份关于起重机的规范( 接口 ),A 公司接下了订单,根据建筑公司起重机的规范和自己的技术,造了起重机 A( 实现 );后面建筑公司和 A 公司不合作,又找了个 B 公司,B 公司也根据规范和自己的技术,造了起重机 B( 实现 ),实则建筑公司在使用起重机的方式和以前一样的。
- go的接口实现示例
package main
import "fmt"
type Crane interface { JackUp() string Hoist() string }
type CraneA struct { name string }
func (c *CraneA) JackUp() string { return c.name +
}
func (c *CraneA) Hoist() string { return c.name +
}
type CraneB struct { name string des string }
func (c *CraneB) JackUp() string { return c.name +
}
func (c *CraneB) Hoist() string { return c.name +
}
type ConstructionCompany struct { name string Crane Crane }
func (c *ConstructionCompany) changeCrane(crane Crane) { c.Crane = crane }
func main() { cc := &ConstructionCompany{"建筑公司", &CraneA{"起重机 A"}} fmt.Println(cc.Crane.JackUp()) fmt.Println(cc.Crane.Hoist()) cc.changeCrane(&CraneB{"起重机 B", "B 公司独家技术专利"}) fmt.Println(cc.Crane.JackUp()) fmt.Println(cc.Crane.Hoist()) }
- 在Go中接口的实现是隐式的,不用像Java使用 implements 关键字实现接口,只要 结构体有接口定义的所有方法,就说明该结构体实现了该接口 。
1.5 空接口
type Any interface{
}
- Any接口内部没有方法集合,按实现的定义,所有的类型都是Any接口的实现,由于所有类型的方法集都是空集的超集,所以 Any 接口可以保存任何类型的值。
2、泛型
2.1 简介
泛型:通过类型参数化来实现代码的复用与灵活性。又称为参数化多态。
作用:使得同一个函数或数据结构可以处理不同类型的数据,而无需为每种类型编写单独的代码。以函数来说,同一个函数,可以处理不同类型的参数。
Go中的泛型:Go是在1.18版本加入对泛型的支持。
2.2 Go泛型能解决的问题简单实例
- 法运算函数,参数和返回值为 int 类型
package main
import "fmt"
func main() { sumValue := sumInt(1, 3) fmt.Println(sumValue) }
func sumInt(a, b int) int { return a +
}
- 当需要一个 float64 类型的加法运算时,需要新增一个参数和返回值为 float64 的函数。后续如果需要其他类型,例如: float32、int8、int16 等参数时,就需要更多的函数,这些函数的处理逻辑是一样的,唯一不同的就只有 参数和返回值 ;或者将 参数和返回值声明为any ,在函数内做判断处理(使用类型断言或反射)。但无论哪种方式,都是麻烦的写法。
package main
import "fmt"
func main() { sumInt := sumInt(1, 3) sumFloat := sumFloat(1.3, 3.4) fmt.Println(sumInt) fmt.Println(sumFloat) }
func sumInt(a, b int) int { return a +
}
func sumFloat(a, b float64) float64 { return a +
}
2.3 使用泛型实现
2.3.1 泛型语法解析
- 泛型的实现
package main
import "fmt"
func main() { sumInt := sum[int](1, 3) sumFloat := sum(1.3, 3.4) fmt.Println(sumInt) fmt.Println(sumFloat) } func sum[T int | float64 | float32](a, b T) T { return a +
}
- Go泛型语法解析
func sum[T int | float64 | float32](a, b T) T {
return a +
}
2.3.2 泛型在其他数据类型上的使用
- 泛型切片
type GenericSlice[T int | int32 | int64] []T
func main() {
v := GenericSlice[int]{1, 2, 3}
fmt.Println(v)
}
- 泛型Map
type GenericMap[K comparable, V int | string | byte] map[K]V
func main() { m := GenericMap[int, string]{1: "hello", 2: "world"} fmt.Println(m) }
- 泛型结构体
type GenericStruct[T int | string] struct {
Name string
Id T
}
func main() { s := GenericStruct[string]{ Name: "hello", Id: "123", } fmt.Println(s) }
- 泛型结构体:泛型切片作为结构体参数
type GenericStructSlice[T int | string, S int | string | float32] struct {
Name string
Id T
slice []S
}
func main() { ss := GenericStructSlice[string, int]{ Name: "hello", Id: "123", slice: []int{1, 2, 3}, } fmt.Println(ss) }
- 泛型接口
type SayAble[T int | string] interface {
Say() T
}
type Person[T int | string] struct { msg T }
func (p Person[T]) Say() T { return p.msg }
func main() { var s SayAble[string] s = Person[string]{"hello world"} fmt.Println(s.Say()) }
2.4 类型集
类型集是Go泛型设计的核心,它定义了一个类型参数可以代表的所有类型的集合,本质上是对接口(interface)语义的扩展。
普通接口(基本接口)描述了 “类型能做什么”(方法集合),而用于泛型的接口(通用接口,即类型约束)描述了 “类型是什么”(类型集合)。
2.4.1 类型集的核心概念
类型集通过 接口类型 来定义,这样的接口在泛型中被称为 类型约束(Type Constraint) 。一个接口作为约束时,它的类型集包含以下所有类型:
- 实现了该接口方法集的所有类型 :这是传统接口的能力,例如 io.Reader 接口的类型集包含所有有 Read(p []byte) (n int, err error) 方法的类型。
简单来说,所有实现了 io.Reader 接口的,都是 io.Reader 的类型集。
func ReadFile[T io.Reader](p []byte,t T) int {
n, err := t.Read(p)
if err != nil {
return n
}
panic("读取错误")
}
- 在接口中显式列出的具体类型 :通过 interface{ int | string | float64 } 这种语法,直接指定类型聚焦包含的类型。
type Num interface {
int | int8 | float32 | float64
}
应用实例:
type Num interface {
int | int8 | float32 | float64
}
func add1[T Num](a, b T) T { return a +
}
func add2[T int | int8 | float32 | float64](a, b T) T { return a +
}
func main() { fmt.Println(add1(1, 2)) fmt.Println(add1(1.2, 2.5))、fmt.Println(add1("1", "2")) }
- 通过 ~T 包含的底层类型为 T 的所有类型 : ~ 符号表明 “底层类型(underlying type)匹配”,例如 ~int 的类型集包含 int 以及所有用 type MyInt int 定义的自定义类型。
type Int1 int
type Int2 Int1
func add1[T int](a, b T) T { return a +
}
func add2[T ~int](a, b T) T { return a +
}
func main() { var v1 Int1 = 1 var v2 Int1 = 1 var v3 Int2 = 1 var v4 Int2 = 1 var v5 int = 1 var v6 int = 1 fmt.Println(add1(v1, v2)) fmt.Println(add1(v3, v4)) fmt.Println(add1(v5, v6))
fmt.Println(add2(v1, v2)) fmt.Println(add2(v3, v4)) fmt.Println(add2(v5, v6))
2.4.2 类型集的并集、交集和空集
- 并集
type Integer interface {
int | int8 | int16 | int32 | int64
}
type UnInteger interface { uint | uint8 | uint16 | uint32 | uint64 }
type UnionInteger interface { Integer | UnInteger }
- 交集
type Integer interface {
int | int8 | int16 | int32 | int64
}
type UnInteger interface { int | uint | uint8 | uint16 | uint32 | uint64 }
type IntersectionInteger interface { Integer UnInteger }
func add[T IntersectionInteger](a, b T) T { return a +
}
func main() { var v1 int = 4 var v2 uint = 8 fmt.Println(add(v1, v1)) fmt.Println(add(v2, v2)) }
–
两个类型集没有交集,就形成了空集。
type Integer interface {
int | int8 | int16 | int32 | int64
}
type UnInteger interface { uint | uint8 | uint16 | uint32 | uint64 }
type EmptyInteger interface { Integer UnInteger }
- 空接口和空集
空接口是所有类型集的集合,即无论什么类型都可以传入,空接口一般用 any 。
空集是任何类型都不能传入。
2.4.3 泛型使用注意事项
- 泛型不能作为一种类型声明。
type Generic[T int | int8] T
type Generic[T int | int8] []T
- 泛型不能做类型断言
泛型主要目的是为了 类型无关 ,如果需要做类型断言,则入参类型应该是 any ,而不是泛型。
func add[T int](a T,c any) T {
a1 := a.(int)
c1 := c.(int)
return a
}
- 匿名结构体和匿名函数不支持使用泛型
匿名结构体和匿名函数在声明时,必须初始化,所以参数类型都必须是已经确定好的。
tempStruct := struct [T int | int8]{
name string
age T
}
f := func[T int | int 8](a T){
}
- 方法的参数不支持泛型,所以泛型只能声明在方法的 接收者 上。-