• Go 接口


    接口

    是什么?为什么?特点?

    在Go语言中,接口定义一系列方法的标签 , 如果某个类型实现了这个接口的所有方法 , 就代表实现了这个接口 , 可以把结构体变量赋给接口变量

    为什么?

    多态:使用相同的操作来处理不同类型的对象

    实现面向对象的多态,通过接口操作多个类型。

    特点?

    隐式接口。不要求类型显示地声明实现了某个接口,只要实现了相关地方法即可,因为编译器能检测到,没有虚函数,继承。

    接口什么情况等于nil?

    接口的动态类型和动态值都为nil时,接口等于nil。

    接口_声明和赋值(多态)

    1. // 定义接口
    2. type Shape interface {
    3. Area() float64
    4. Perimeter() float64
    5. }
    6. // 实现接口:圆形
    7. type Circle struct {
    8. Radius float64
    9. }
    10. func (c Circle) Area() float64 {
    11. return math.Pi * c.Radius * c.Radius
    12. }
    13. func (c Circle) Perimeter() float64 {
    14. return 2 * math.Pi * c.Radius
    15. }
    16. // 实现接口:矩形
    17. type Rectangle struct {
    18. Width float64
    19. Height float64
    20. }
    21. func (r Rectangle) Area() float64 {
    22. return r.Width * r.Height
    23. }
    24. func (r Rectangle) Perimeter() float64 {
    25. return 2*r.Width + 2*r.Height
    26. }
    27. func main() {
    28. // 创建圆形和矩形对象
    29. circle := Circle{Radius: 5}
    30. rectangle := Rectangle{Width: 3, Height: 4}
    31. fmt.Println(circle.Area(), circle.Perimeter())
    32. fmt.Println(rectangle.Area(), rectangle.Perimeter())
    33. }

     实现fmt包中的Stringer接口

    1. type Person struct {
    2. Name string
    3. Age int
    4. }
    5. func (p Person) String() string {
    6. return fmt.Sprintf("%s is %d years old", p.Name, p.Age)
    7. }
    8. func main() {
    9. person := Person{Name: "Alice", Age: 30}
    10. fmt.Println(person) // 这里会自动调用 String() 方法输出 "Alice is 30 years old"
    11. }

    接口_比较

    在Go语言中,一个接口被认为是nil,其类型和值都必须是nil。 

    运行时候,接口被实现为一堆指针,一个指向底层类型,一个指向基础值。

    1. // 接口值在动态类型和动态值都等于nil时等于nil
    2. type Coder interface {
    3. code()
    4. }
    5. type Gopher struct {
    6. name string
    7. }
    8. func (g Gopher) code() {
    9. fmt.Printf("%s is coding\n", g.name)
    10. }
    11. func main() {
    12. var c Coder
    13. fmt.Println(c == nil)
    14. fmt.Printf("c: %T, %v\n", c, c)
    15. var g *Gopher
    16. fmt.Println(g == nil)
    17. c = g
    18. fmt.Println(c == nil)
    19. fmt.Printf("c: %T, %v\n", c, c)
    20. }
    1. func main() {
    2. var s *string
    3. fmt.Println(s == nil)
    4. var i interface{}
    5. var i2 interface{}
    6. fmt.Println(i == nil)
    7. fmt.Println(i == i2)
    8. fmt.Println(s == i)
    9. }
    1. /*
    2. 在 Go 语言中,当你尝试在一个 nil 接口值上调用方法时,会引发运行时错误(panic)。这种情况通常会发生在接口的底层值为 nil 的情况下。 
    3. */
    4. type Shape interface {
    5. Area() float64
    6. }
    7. type Circle struct {
    8. Radius float64
    9. }
    10. func (c Circle) Area() float64 {
    11. return 3.14 * c.Radius * c.Radius
    12. }
    13. func main() {
    14. var s Shape
    15. var c *Circle // c 是一个指向 Circle 类型的指针变量,但尚未初始化
    16. s = c // 将一个指向 Circle 的 nil 指针赋值给接口 s
    17. fmt.Println(c == nil)
    18. fmt.Println(s == nil)
    19. fmt.Println(s.Area()) // 这里会引发 panic,因为 s 是一个 nil 接口值
    20. }

     接口转换

     定义一个MyError结构体,error接口。
    Process函数返回一个error接口。

    1. // 隐含的类型转换.动态类型不一致
    2. type MyError struct {
    3. }
    4. func (e MyError) Error() string {
    5. return "MyError"
    6. }
    7. func Process() error {
    8. return MyError{}
    9. }
    10. func main() {
    11. e := Process()
    12. fmt.Println(e == nil)
    13. }

    接口_接口在函数中传递(依赖注入)

    在 Go 中,依赖注入是一种设计模式,用于将对象的依赖项(或依赖关系)从对象本身中解耦。利用接口和依赖注入,可以在运行时传递依赖项,允许在不修改现有代码的情况下替换具体的实现。

    在 Go 语言中,可以在函数中传递接口。这种方法使得函数能够接受满足特定接口定义的任何类型。通过接口,可以实现对不同类型的对象进行抽象处理。

    1. // 定义接口
    2. type Shape interface {
    3. Area() float64
    4. Perimeter() float64
    5. }
    6. // 实现接口:圆形
    7. type Circle struct {
    8. Radius float64
    9. }
    10. func (c Circle) Area() float64 {
    11. return math.Pi * c.Radius * c.Radius
    12. }
    13. func (c Circle) Perimeter() float64 {
    14. return 2 * math.Pi * c.Radius
    15. }
    16. // 实现接口:矩形
    17. type Rectangle struct {
    18. Width float64
    19. Height float64
    20. }
    21. func (r Rectangle) Area() float64 {
    22. return r.Width * r.Height
    23. }
    24. func (r Rectangle) Perimeter() float64 {
    25. return 2*r.Width + 2*r.Height
    26. }
    27. func printShapeInfo(s Shape) {
    28. fmt.Println("Area:", s.Area())
    29. fmt.Println("Perimeter:", s.Perimeter())
    30. }
    31. func main() {
    32. // 创建圆形和矩形对象
    33. circle := Circle{Radius: 5}
    34. rectangle := Rectangle{Width: 3, Height: 4}
    35. printShapeInfo(circle)
    36. printShapeInfo(rectangle)
    37. }

     接口_接口在函数中返回

    这种方法允许函数返回某个结构体或类型的实例,并以接口的方式暴露出来,这样可以隐藏具体类型的实现细节。

    1. package main
    2. import (
    3. "fmt"
    4. )
    5. // 定义一个接口 Shape
    6. type Shape interface {
    7. getName() string
    8. }
    9. // 定义 Circle 结构体,并实现 Shape 接口的 getName 方法
    10. type Circle struct {
    11. radius float64
    12. }
    13. func (c Circle) getName() string {
    14. return "Circle"
    15. }
    16. // 创建一个函数 createCircle 返回 Shape 接口
    17. func createCircle() Shape {
    18. return Circle{radius: 5}
    19. }
    20. func main() {
    21. circle := createCircle() // 返回的是 Shape 接口,指向 Circle 结构体实例
    22. // 通过 Shape 接口调用 getName() 方法
    23. fmt.Println("Circle's name:", circle.getName())
    24. }

    接口_结构体内嵌接口(委托模式)

    结构体内嵌接口

    委托模式这意味着一个对象将其特定的职责委托给另一个对象来处理。
    在 Go 中,结构体嵌入结构体或者方法的方式可以实现委托。

    1. package main
    2. import "fmt"
    3. // Animal 接口定义了动物发出声音的方法
    4. type Animal interface {
    5. MakeSound() string
    6. }
    7. // Dog 结构体代表狗
    8. type Dog struct{}
    9. func (d Dog) MakeSound() string {
    10. return "Woof!"
    11. }
    12. // Cat 结构体代表猫
    13. type Cat struct{}
    14. func (c Cat) MakeSound() string {
    15. return "Meow!"
    16. }
    17. // Pet 结构体代表宠物,委托 Animal 接口
    18. type Pet struct {
    19. animal Animal
    20. }
    21. func (p Pet) MakeSound() string {
    22. return p.animal.MakeSound()
    23. }
    24. func main() {
    25. dog := Dog{}
    26. cat := Cat{}
    27. petDog := Pet{animal: dog}
    28. petCat := Pet{animal: cat}
    29. fmt.Println("Dog says:", petDog.MakeSound()) // 委托给狗对象
    30. fmt.Println("Cat says:", petCat.MakeSound()) // 委托给猫对象
    31. }

    接口_接口内嵌接口

    在 Go 中,接口可以嵌入到其他接口,形成一种接口组合的结构。这种方式可以用于构建更大、更复杂的接口,将多个小的接口组合成一个更大的接口。 

    1. // Sound 接口定义了动物发出声音的方法
    2. type Sound interface {
    3. Sound() string
    4. }
    5. // Move 接口定义了动物的移动方法
    6. type Move interface {
    7. Move() string
    8. }
    9. // SoundMove 接口内嵌了 Sound 和 Move 接口
    10. type SoundMove interface {
    11. Sound
    12. Move
    13. }
    14. type Dog struct{}
    15. func (d Dog) Sound() string {
    16. return "Woof!"
    17. }
    18. func (d Dog) Move() string {
    19. return "Running"
    20. }
    21. func main() {
    22. var soundMove SoundMove
    23. soundMove = Dog{}
    24. fmt.Println(soundMove.Sound())
    25. fmt.Println(soundMove.Move())
    26. }

    接口_函数类型实现接口

    1. // 定义一个接口
    2. type Greeter interface {
    3. Greet() string
    4. }
    5. // 定义一个函数类型
    6. type GreetingFunc func() string
    7. // 实现接口的方法
    8. func (g GreetingFunc) Greet() string {
    9. return g()
    10. }
    11. func main() {
    12. // 定义一个函数,返回字符串 "Hello, World!"
    13. hello := func() string {
    14. return "Hello, World!"
    15. }
    16. // 将函数转换为 GreetingFunc 类型
    17. greetingFunc := GreetingFunc(hello)
    18. // 将函数类型变量传递给接口类型
    19. var greeter Greeter = greetingFunc
    20. // 调用接口方法
    21. fmt.Println(greeter.Greet()) // 输出 "Hello, World!"
    22. }

    接口_接受接口,返回结构体

    接口最佳实践?

    error规则是一个例外情况,需要返回不同的error实现。


    接口_接口实现

    实现接口io.Reader 

    1. package main
    2. import (
    3. "fmt"
    4. "io"
    5. "io/ioutil"
    6. "log"
    7. )
    8. // MyReader 是自定义的读取器类型
    9. type MyReader struct {
    10. data []byte // 存储要读取的数据
    11. current int // 当前读取位置
    12. }
    13. // NewMyReader 创建一个 MyReader 实例
    14. func NewMyReader(data []byte) *MyReader {
    15. return &MyReader{
    16. data: data,
    17. current: 0,
    18. }
    19. }
    20. // Read 实现了 Reader 接口的 Read 方法
    21. func (r *MyReader) Read(p []byte) (int, error) {
    22. if r.current >= len(r.data) { // 如果已经读取完所有数据
    23. return 0, io.EOF // 返回读取字节数为 0,同时返回 io.EOF 表示已经到达文件末尾
    24. }
    25. n := copy(p, r.data[r.current:]) // 将数据从 r.data 复制到 p 中,返回实际复制的字节数
    26. r.current += n // 更新当前读取位置
    27. return n, nil // 返回复制的字节数和没有错误
    28. }
    29. func main() {
    30. data := []byte("Hello, World!")
    31. reader := NewMyReader(data) // 创建 MyReader 实例,用于读取 data
    32. b, err := ioutil.ReadAll(reader) // 使用 ioutil 包的 ReadAll 函数读取全部数据
    33. if err != nil {
    34. log.Fatal(err)
    35. }
    36. fmt.Printf("%s\n", b)
    37. }

    实现接口io.Write 

     ioutil.ReadAll(reader) 相当于通用方法,是用于从实现了 io.Reader 接口的对象中读取所有数据的操作。
    这种通用的方法可以用于各种情况,例如从文件、网络连接、HTTP 响应等读取数据。无论数据源是什么,只要实现了 io.Reader 接口,就可以使用 ioutil.ReadAll 来读取数据。这种做法使得代码编写更加灵活和通用,无需针对不同类型的数据源编写不同的读取逻辑。
     

    1. package main
    2. import (
    3. "fmt"
    4. "io"
    5. )
    6. type MyWriter struct {
    7. data []byte
    8. }
    9. func NewMyWriter() *MyWriter {
    10. return &MyWriter{}
    11. }
    12. func (w *MyWriter) Write(p []byte) (int, error) {
    13. w.data = append(w.data, p...)
    14. return len(p), nil
    15. }
    16. func main() {
    17. writer := NewMyWriter()
    18. writer.Write([]byte("Hello, "))
    19. writer.Write([]byte("World!"))
    20. fmt.Println("Data:", string(writer.data))
    21. io.WriteString(writer, "Hello World")
    22. fmt.Printf("%v\n", string(writer.data))
    23. }

    实现接口error

    1. package main
    2. import "fmt"
    3. type User struct {
    4. }
    5. func (u User) Error() string {
    6. return "错误"
    7. }
    8. func main() {
    9. var e error
    10. e = User{}
    11. fmt.Println(e.Error())
    12. }

  • 相关阅读:
    共载多西紫杉醇/依克立达人血清白蛋白纳米粒|白蛋白纳米粒可以包载多种药物
    Polar SI9000阻抗计算
    第3章 内存管理
    Node.js 实现抢票小工具&短信通知提醒
    干货分享——银行运维组织如何转向敏捷?
    linux用户及用户组的分类、管理
    爬虫笔记15——爬取网页数据并使用redis数据库set类型去重存入,以爬取芒果踢V为例
    动态生成表格完整版(内含解析)
    聊聊运营活动的设计与实现逻辑
    DraSearchTools...
  • 原文地址:https://blog.csdn.net/w1834938347/article/details/134399662