go接口

77 阅读4分钟

go接口

定义

接口类型是对其他类型行为的抽象和概括,因为接口类型不会和特定的实现细节绑定在一起。

接口类型集

  • 一个接口类型定义了一些类型条件,所有满足了这些类型条件的非接口类型形成了一个类型集合,此类型集合称为接口类型的类型集。

类型条件的支持

  • 方法元素
    • 对应的函数签名
  • 类型元素
    • 一个类型名称
    • 一个类型字面表示形式
    • 一个近视类型或者一个类型的并集
type ReadWriteCloser = interface {
    Read(buf []byte) (n int, err error)
    Write(buf []byte) (n int, err error)
    error        // 1. 一个类型名称
    interface {Close() error} // 2. 一个类型字面表示形式
}

type AnyByteSlice = interface {
    ~[]byte    //3. 一个近视类型,它的类型集由所有底层类型为[]byte的类型组成
}

// 4. 此接口类型内嵌了一个类型并集,它的类型集合包含6个类型
type Unsigned interface {
    uint | uint8 | uint16 | uint32 | uint64 | uintptr
}

接口隐式实现的优点

  • 低耦合
  • 一个声明在另外一个代码包(标准库包)中的类型可以被动地实现一个用户代码包中的接口类型

注意

  • 因为空接口类型的类型集包含了所有的非接口类型,所以所有类型均实现了空接口类型。 这是Go中的一个重要事实。

值包裹

接口值的类型必须为一个基本接口类型,也就是说一个值类型被提及,此值类型可能是一个非接口类型,也可能是一个基本接口类型,但它肯定不是一个非基本接口类型

type Content interface {
   About() string
}

type Book struct {
   Name string
}

func (b *Book) About() string {
   return "Book: " + b.Name
}

func main() {
   // 一个*Book被包裹在Content接口中
   var a Content = &Book{Name: "English"}
   fmt.Println(a)

   // 类型*Book实现了任务空接口
   var c interface{} = &Book{Name: "Chinese"}
   fmt.Println(c)
}

// output:
//&{English}
//&{Chinese}

多态

为不同数据类型的实体提供统一的接口,在go语言中表示一个接口值可以通过包裹不同动态类型的动态值来表现出各种不同的行为,这种称为多态

package main

import "fmt"

func main() {
   fmt.Println(introduce(new(Cow)))
   fmt.Println(introduce(new(Wolf)))
   // output:
   // my name is niu niu, I love eat grass!
   // my name is wo fu, I love eat meat!
}

type Animal interface {
   Name() string
   Eat() string
}

func introduce(anl Animal) string {
   return fmt.Sprintf("%s, %s", anl.Name(), anl.Eat())
}

type Cow struct {
}

func (c *Cow) Name() string {
   return "my name is niu niu"
}

func (c *Cow) Eat() string {
   return "I love eat grass!"
}

type Wolf struct {
}

func (w Wolf) Name() string {
   return "my name is wo fu"
}

func (w Wolf) Eat() string {
   return "I love eat meat!"
}
  • 鸭子类型

反射

提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道这些变量的具体类型,这种称为反射机制

内置的反射机制

断言(type assertion)

  • 断言表达式 i.(T)

    • T为任意非接口类型
    • 或者一个任意接口类型
func main() {
   var x interface{} = 456

   // 情况一
   n, ok := x.(int)
   fmt.Println(n, ok) //output: 456  true
   n = x.(int)        //忽略错误断言
   fmt.Println(n)     // output: 456

   // 情况二
   a, ok := x.(float32)
   fmt.Println(a, ok) // output: 0 false

   // 情况三
   a = x.(float32) // output: panic: interface conversion: interface {} is int, not float32
}
  • 自定义接口类型
type Writer interface {
   Write(buf []byte) (int, error)
}

type MyWriter struct {
}

func (m MyWriter) Write(buf []byte) (int, error) {
   return len(buf), nil
}

func main() {
   var x interface{} = MyWriter{}
   var y interface{} = "abc"

   // 情况一
   w, ok := x.(Writer)
   fmt.Println(w, ok) //output: {} true
   x, ok = w.(interface{})
   fmt.Println(x, ok) // output: {} true

   // 情况二
   w, ok = y.(Writer)
   fmt.Println(w, ok) //output: false
   w = y.(Writer)     // output: panic
}

类型选择(type-switch)

  • 语法 `x.(type)`
func main() {
   values := []interface{}{456, "abc", true, 1.33, []int{1, 2, 3}, map[int]bool{}, nil}

   for _, x := range values {
      switch x.(type) {
      case int, float32: //多个类型名
         fmt.Println("number:", x)
      case string:
         fmt.Println("string:", x)
      case bool:
         fmt.Println("bool:", x)
      case []int:
         fmt.Println("[]int: ", x)
      case map[int]bool:
         fmt.Println("map: ", x)
      case nil:
         fmt.Println("nil: ", x)
      default:
         fmt.Println("others: ", x)
      }
   }

   //output:
   //number: 456
   //string: abc
   //bool: true
   //others:  1.33
   //[]int:  [1 2 3]
   //map:  map[]
   //nil:  <nil>
}

类型约束

func main() {
   m, n := Sum(1, 2, 3, 4, 5, 6, 7, 8, 9), Sum[float32](1, 2, 3, 4, 5, 6, 7, 8, 9.1)
   fmt.Printf("%d %v\n", m, reflect.TypeOf(m))
   fmt.Printf("%f %v\n", n, reflect.TypeOf(n))
   // output:
   // 45 int
   // 45.099998 float32
}

// M 类型约束
type M interface {
   int | int8 | int16 | int32 | int64 | float32
}

func Sum[T M](nums ...T) T {
   var res T
   for _, n := range nums {
      res += n
   }
   return res
}