0x00

这次golang正式更新了泛型(之前的1.17 和1.18 Beta 1 中的只是测试版,其中有一些用法和正式版有较大的差别),是一次重大的更新,原以为在GO2.0的时候更新。

0x01 类型形参(Type Parameters)

在之前想要实现一些通用的功能还是比较麻烦的,而且代码冗余。

比如实现gorm添加json类型字段:

type Table struct {
  AField    A
  BField    B
}

type A struct {
    AText string
}
func (A) GormDataType() string {
    return "json"
}
func (a *A) Scan(value interface{}) error {
    return json.Unmarshal(value.([]byte), a)
}
func (a A) Value() (value driver.Value, err error) {
    src, err := json.Marshal(a)
    return string(src), err
}

type B struct {
    BText string
}
func (B) GormDataType() string {
    return "json"
}
func (b *B) Scan(value interface{}) error {
    return json.Unmarshal(value.([]byte), b)
}
func (b B) Value() (value driver.Value, err error) {
    src, err := json.Marshal(b)
    return string(src), err
}

可以看到AB中的GormDataType Scan Value方法几乎都是重复的,假如再添加C D那么就要添加6个重复发方法。

<br/>

而golang 1.8开始正式支持泛型,那么利用泛型可以降低这样的代码冗余

使用泛型再次实现上述内容:

type Table struct {
  AField    JSON[A]
  BField    JSON[B]
}

type A struct {
  AText string
}

type B struct {
  BText string
}


type JSON[T any] struct {
    Data T
}
func (JSON[T]) GormDataType() string {
    return "json"
}
func (m *JSON[T]) Scan(value interface{}) error {
    return json.Unmarshal(value.([]byte), &m.Data)
}
func (m JSON[T]) Value() (value driver.Value, err error) {
    src, err := json.Marshal(m.Data)
    return string(src), err
}

可以看到我这里定义了一个JSON[T any]的类型专门处理json类型,此后添加C D字段就只需要定义相关的结构体并且添加JSON[C] JSON[D]即可。

<br/>

其中any类型也是这次更新后的新类型字段,但any其实就是interface类型的别称

源码:

// src/builtin/builtin.go 95行

// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}

个人感觉any相比interface在传参等场合更直观

比如:

func Println(v ...any){}

func Println(v ...interface){}

当然,在定义接口时还是得用interface字段

<br/>

判断类型

func (m *JSON[T]) Test() {
    switch any(m.Data).(type) {
      case A:
        fmt.Println("A")
      case B:
        fmt.Println("B")
    }
}

#1014950980

0x02 类型集合(Type Sets)

类型集合允许定义一个类型具有不同类型的集合

例如:

type Test interface {
  int|float64
}

定义了一个Test类型,表示为int float64类型的集合

<br/>

如果不用泛型那怎么写呢

不使用泛型:

func Add(a, b interface{}) interface{} {
    switch a.(type) {
    case int:
        return a.(int)+b.(int)
    case float64:
        return a.(float64)+b.(float64)
    }
    return 0
}

使用泛型:

func Add[T int | float64](a, b T) T {
    return a + b
}

这样一对比使用泛型就简洁多了

<br/>

应用:

type Num interface {
    int | int64 | int32 |
        float64 | float32 |
        uint | uint8 | uint16 | uint64
}

func Add[T Num](a, b T) T {
    return a + b
}

func Sub[T Num](a, b T) T {
    return a - b
}

<br/>

0x03 类型约束

type T interface {
  int | float64 | ~string
}

其中的~string表示定义的T类型集合也支持底层类型是string的类型

比如:

type A interface {
  ~string
}

func Splic[T A](a, b T) T {
  return a + b
}

var BString string

func main() {
  var a string = "a"
  var b BString = "b"
  
  Splic(a, b)
}

其中BString是自定义的一个底层为string的类型,如果没有加~的话会报错

Last modification:April 29, 2022
如果觉得我的文章对你有用,请随意赞赏