十二、Go语法进阶(接口和泛型)

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){

}
  • 方法的参数不支持泛型,所以泛型只能声明在方法的 接收者 上。-
© 版权声明

相关文章

暂无评论

none
暂无评论...