Golang笔记——Interface类型

大家好,这里是,关注 公主号:Goodnote,专栏文章私信限时Free。本文详细介绍Golang的interface数据结构类型,包括基本实现和使用等。

在这里插入图片描述

文章目录
      • Go 语言中的 `interface` 详解
      • 接口定义
      • 实现接口
      • 空接口 `interface{}`
        • 示例:空接口的使用
      • interface 类型判断
      • 1. 类型断言(Type Assertion)
        • 语法
        • 示例
        • 类型转换和类型断言的区别
      • 2. 类型开关(Type Switch)
        • 语法
        • 示例
      • 3. 反射(Reflection)
        • 示例
        • 反射的关键点
      • 总结
      • 接口与多态
        • 解释
      • 接口的嵌套
        • 示例
      • 总结
    • 历史文章
      • MySQL数据库
      • Redis
      • Golang
Go 语言中的 interface 详解

Go 语言中的 interface 是一种强大且灵活的类型系统机制,它使得 Go 能够实现类似面向对象语言多态的特性。接口是一组方法签名的集合,而接口类型定义了某些行为,任何类型只要实现了接口中的方法,就自动成为该接口的实现类型。

简单的说,interface主要表现在以下几个方面:

  1. 方法的集合:接口是方法的集合,定义了一个类型需要实现哪些方法,但不关心实现的具体细节。只要一个类型实现了接口中的方法,就被认为实现了这个接口。
  2. 多态实现:接口的核心作用之一是实现多态。在 Go 中,不同类型只要实现了相同的接口方法,就可以通过接口来统一处理,达到灵活的多态性。这使得我们可以编写更加解耦和可扩展的代码。
  3. 空接口 interface{}是一种变量类型:接口是 Go 中的一个类型,空接口(interface{}) 是最基础的接口类型,它可以存储任何类型的值。实际上它是一个由两部分组成的结构体:
    • 类型(Dynamic Type):接口变量所保存值的具体类型。
    • 值(Dynamic Value):接口变量所保存的具体值。

在 Go 中,接口是非常核心的概念,它帮助我们编写解耦、灵活、可扩展的代码。

接口定义

接口定义了类型应该具备的行为(即方法)。Go 的接口与其他语言(如 Java 或 C++)中的接口有一些不同之处,特别是Go 的接口不需要显式声明实现,即只要类型实现了接口的方法,就自动实现了该接口。

type InterfaceName interface {
    Method1() // 方法签名
    Method2() // 方法签名
}
实现接口

Go 不要求显式声明某个类型实现了一个接口,只要该类型实现了接口中声明的所有方法,它就自然地“实现”了该接口。接口与类型之间的关系是隐式的。

package main

import "fmt"

// 定义接口
type Speaker interface {
    Speak() // 定义接口中的方法
}

// 定义结构体
type Person struct {
    Name string
}

// Person 实现了 Speaker 接口的 Speak 方法
func (p Person) Speak() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    // 创建 Person 类型的实例
    p := Person{Name: "Alice"}

    // 将 p 赋给接口类型 Speaker
    var speaker Speaker = p

    // 调用接口的方法
    speaker.Speak()  // 输出:Hello, my name is Alice
}

说明:

  • Speaker 接口有一个方法 Speak
  • Person 类型实现了 Speak 方法,因此它自动实现了 Speaker 接口。
  • main 函数中,p 被赋给了接口类型 Speaker,然后调用接口的方法 Speak
空接口 interface{}

空接口 interface{} 是一个特殊的接口类型,它没有定义任何方法。由于任何类型都实现了空接口。空接口通常用于存储任何类型的值,类似于其他语言中的 Object 类型。

示例:空接口的使用
package main

import "fmt"

func main() {
	var x interface{} // 声明一个空接口
	x = 42            // x 可以存储 int
	fmt.Println(x)    // 输出:42

	x = "Hello"    // x 可以存储 string
	fmt.Println(x) // 输出:Hello

	x = 3.14       // x 可以存储 float64
	fmt.Println(x) // 输出:3.14
}
interface 类型判断

在 Go 中,由于接口类型是通用的,它可以存储任何实现了该接口的类型,因此在使用接口时,可能并不知道它具体存储的是哪个类型的值。为了处理这种不确定性,Go 提供了三种常用的机制来检测或转换接口的实际类型:

  1. 类型断言(Type Assertion)
  2. 类型开关(Type Switch)
  3. 反射(Reflection)

这三者在 Go 中各有不同的用途,适用于不同的场景。通常情况下,如果你只需要判断一个接口的类型并进行相应处理,类型断言类型开关 是更常见的选择;而 反射 则通常用于更加动态和通用的场景,例如实现框架、库、ORM 等。

下面分别详细介绍这三种机制:

1. 类型断言(Type Assertion)

类型断言用于从接口类型转换回具体类型。它允许我们在运行时检查接口值的动态类型,并进行转换。

语法
value, ok := x.(T)
  • x 是接口类型的变量,T 是你想要转换成的具体类型。
  • 如果 x 存储的值是 T 类型,value 将会是存储的值,而 oktrue
  • 如果 x 存储的值不是 T 类型,value 会是 T 类型的零值,而 okfalse
示例
package main

import "fmt"

func main() {
    var a interface{} = 42

    // 类型断言
    if v, ok := a.(int); ok {
        fmt.Println("a is an int:", v)
    } else {
        fmt.Println("a is not an int")
    }
}

输出:

a is an int: 42
类型转换和类型断言的区别
  • 类型转换 是编译时的操作,用于在兼容类型之间进行显式转换。它适用于基本类型之间以及自定义类型的转换。
  • 类型断言 是运行时的操作,用于从接口类型中提取具体的类型和值。它适用于动态类型判断的场景。

属性

类型转换 (Type Conversion)

类型断言 (Type Assertion)

使用场景

在兼容的类型之间进行显式转换(如 intfloat64)。

用于从接口类型中提取底层的具体类型和值。

操作时机

编译时。

运行时。

适用范围

基本数据类型、自定义类型等。

接口类型(interface{} 或其他接口)。

结果

将值转换为目标类型。

提取接口的具体值或判断类型是否匹配。

错误处理

不兼容类型转换会导致编译错误。

不安全断言会导致运行时 panic;安全断言返回一个布尔值。

语法

value := T(originalValue)

value, ok := iface.(T)value := iface.(T)

2. 类型开关(Type Switch)

类型开关是 Go 中提供的更强大、更灵活的机制,它允许我们对接口值的类型进行多分支判断。与普通的 switch 语句不同,类型开关的 case 中是基于接口的动态类型进行匹配的。

语法
switch v := x.(type) {
case T1:
    // x 是 T1 类型
case T2:
    // x 是 T2 类型
default:
    // x 是其他类型
}
  • x.(type) 会检查 x 接口的动态类型。
  • 你可以根据不同的类型执行不同的逻辑。
示例
package main

import "fmt"

func identifyType(x interface{}) {
    switch v := x.(type) {
    case int:
        fmt.Println("int:", v)
    case string:
        fmt.Println("string:", v)
    default:
        fmt.Println("unknown type")
    }
}

func main() {
    identifyType(42)        // 输出:int: 42
    identifyType("hello")    // 输出:string: hello
    identifyType(3.14)       // 输出:unknown type
}
3. 反射(Reflection)

Go 的 reflect 包提供了在运行时操作接口的功能,能够动态地获取接口的具体类型和方法。这是 Go 中非常强大的特性,可以在不知道类型的情况下执行一些操作,例如获取类型的名称、字段信息、调用方法等。

示例
package main

import (
    "fmt"
    "reflect"
)

type Dog struct{}

func (d Dog) Speak() {
    fmt.Println("Woof!")
}

func main() {
    var a interface{} = Dog{}
    
    // 使用反射获取类型
    t := reflect.TypeOf(a)
    fmt.Println("Type:", t)

    // 使用反射获取值
    v := reflect.ValueOf(a)
    fmt.Println("Value:", v)

    // 通过反射调用方法
    if t.Kind() == reflect.Struct {
        method := v.MethodByName("Speak")
        if method.IsValid() {
            method.Call(nil)  // 输出:Woof!
        }
    }
}
反射的关键点
  • reflect.TypeOf() 用来获取接口的具体类型。
  • reflect.ValueOf() 用来获取接口的具体值。
  • reflect.ValueOf(a).MethodByName("MethodName") 可以动态调用结构体的方法
总结
  • 类型断言:用于在运行时提取接口的具体类型值,如果类型不匹配,可以使用 ok 变量避免运行时错误。
  • 类型开关:允许你对接口值的动态类型进行多分支判断,可以在多个可能的类型之间选择。
  • 反射:通过 reflect 包可以在运行时获取接口的类型和值,甚至可以动态地调用方法或修改值
接口与多态

Go 语言的多态是通过接口实现的。接口提供了一种方法,让不同类型的对象能以统一的方式来调用它们的行为。

package main

import "fmt"

type Animals interface {
	Say()
}

type Dog struct{}
type Cat struct{}

func (d Dog) Say() {
	fmt.Println("wangwang")
}

func (c Cat) Say() {
	fmt.Println("miaomiao")
}

func main() {
	var d Dog
	d.Say() // 输出:wangwang

	var c Cat
	c.Say() // 输出:miaomiao

	// 使用接口变量1,可以接受任何实现了 Say() 方法的类型
	var a Animals
	a = d
	a.Say() // 输出:wangwang
	a = c
	a.Say() // 输出:miaomiao

	// 使用接口变量2,可以接受任何实现了 Say() 方法的类型
	var a1 Animals
	a1 = Dog{}
	a1.Say() // 输出:wangwang
	a1 = Cat{}
	a1.Say() // 输出:miaomiao
}
解释
  • DogCat 都实现了 Animals 接口。
  • a 是一个接口类型,可以存储任何实现了 Say 方法的类型。
  • 通过多态,我们可以使用同一个接口变量 a 存储不同的类型,并调用它们各自的 Say 方法。
接口的嵌套

Go 允许接口嵌套,接口可以继承其他接口的方法。当一个接口嵌套另一个接口时,它自动包含了被嵌套接口的方法。

示例
package main

import "fmt"

// 定义 Animal 接口
type Animal interface {
    Speak()
}

// 定义 Worker 接口,嵌入 Animal 接口
type Worker interface {
    Animal  // Animal 接口被嵌套在 Worker 接口中
    Work()
}

// 定义 Dog 结构体
type Dog struct{}

// Dog 实现了 Animal 接口的 Speak 方法
func (d Dog) Speak() {
    fmt.Println("wangwang")
}

// Dog 实现了 Worker 接口的 Work 方法
func (d Dog) Work() {
    fmt.Println("Dog is working!")
}

func main() {
    // 创建 Dog 类型的对象
    var w Worker = Dog{}
    
    // 调用 Worker 接口的方法
    w.Speak() // 输出:wangwang
    w.Work()  // 输出:Dog is working!
}
  1. 接口嵌套Worker 接口通过 Animal 接口嵌套了 Speak 方法,这意味着 Worker 接口需要实现 SpeakWork 方法。

  2. Dog 类型实现接口Dog 类型实现了 SpeakWork 方法,满足了 Worker 接口的要求。

  3. 接口引用:在 main 函数中,wWorker 类型的接口变量,它引用了 Dog 类型的对象。由于 Dog 类型实现了 SpeakWork 方法,所以可以调用 w.Speak()w.Work()

总结
  • Go 中的接口:接口是一组方法签名的集合,Go 语言通过接口实现了多态。
  • 隐式实现:Go 中不需要显式声明类型实现接口,任何实现了接口方法的类型都会自动实现该接口。
  • 空接口interface{} 可以存储任何类型的值,类似于其他语言中的 Object 类型。
  • 类型断言:允许从接口类型转换回具体类型,提供灵活的运行时类型检查。
  • 接口与多态:通过接口,Go 实现了动态多态,允许不同类型通过统一的接口调用各自的行为。

接口是 Go 语言的核心特性之一,它使得 Go 在保持简洁和灵活性的同时,支持面向对象的编程风格。

历史文章

MySQL数据库

MySQL数据库

Redis

Redis数据库笔记合集

Golang
  1. Golang笔记——语言基础知识
  2. Golang笔记——切片与数组
  3. Golang笔记——hashmap
  4. Golang笔记——rune和byte
  5. Golang笔记——channel

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/977282.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker容器网络配置及常用操作

Linux内核实现名称空间的创建 ip netns(网络名称空间)命令 可以借助ip netns命令来完成对 Network Namespace 的各种操作。ip netns命令来自于iproute安装包,一般系统会默认安装,如果没有的话,请自行安装。 注意&am…

leetcode - hot100 - python - 专题二:双指针

1、移动0 (一句话概括题眼:右指针找非0元素) 简单 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例…

【玩转 Postman 接口测试与开发2_020】(完结篇)DIY 实战:随书示例 API 项目本地部署保姆级搭建教程(含完整调试过程)

《API Testing and Development with Postman》最新第二版封面 文章目录 最新版《Postman 接口测试与开发实战》示例 API 项目本地部署保姆级搭建教程1 前言2 准备工作3 具体部署3.1 将项目 Fork 到自己名下3.2 创建虚拟环境并安装依赖3.3 初始运行与项目调试 4 示例项目的用法…

【第五节】C++设计模式(创建型模式)-Prototype(原型)模式

目录 一、问题背景 二、 模式选择 三、讨论总结 一、问题背景 在软件开发中,有时我们需要通过已有对象来创建新对象,而不是从头开始构建。这种需求让我想起了现代制造业中的 3D 打印技术。通过扫描一个现有的物体,3D 打印机可以快速复制出…

next.js-学习2

next.js-学习2 1. https://nextjs.org/learn/dashboard-app/getting-started2. 模拟的数据3. 添加样式4. 字体,图片5. 创建布局和页面页面导航 1. https://nextjs.org/learn/dashboard-app/getting-started /app: Contains all the routes, components, and logic …

OpenCV计算摄影学(1)图像修复(Inpainting)的函数inpaint()

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 使用图像中选定区域的邻域来恢复该选定区域。 cv::inpaint 函数是 OpenCV 中用于图像修复(Inpainting)的一个重要函数。它…

北京智和信通:全方位智能 OLT、ONU 设备监控运维方案

随着网络技术的不断迭代与发展,OLT作为光纤接入网中的核心设备,负责管理多个ONU,实现数据的传输和分配。其监控与运维的重要性愈发凸显,为了确保网络运行的高效与稳定,选择一套全面且高效的OLT、ONU监控运维方案显得尤…

python-leetcode-搜索二维矩阵 II

240. 搜索二维矩阵 II - 力扣(LeetCode) class Solution:def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:if not matrix or not matrix[0]:return Falsem, n len(matrix), len(matrix[0])i, j 0, n - 1 # 从右上角开始whi…

推送项目 之 解决冲突

文章目录 为什么会发生冲突?如何解决这些冲突?1. **查看冲突文件**2. **解决二进制文件冲突**3. **解决文本文件冲突**4. **标记冲突已解决**5. **完成合并**6. **推送更改** 注意事项总结 问题:我们在git pusll拉取远程仓库的代码到本地对比…

网页版的俄罗斯方块

1、新建一个txt文件 2、打开后将代码复制进去保存 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>俄…

Docker 部署 Jenkins持续集成(CI)工具

[TOC](Docker 部署 Jenkins持续集成(CI)工具) 前言 Jenkins 是一个流行的开源自动化工具&#xff0c;广泛应用于持续集成&#xff08;CI&#xff09;和持续交付&#xff08;CD&#xff09;的环境中。通过 Docker 部署 Jenkins&#xff0c;可以简化安装和配置过程&#xff0c;并…

LLM+多智能体协作:基于CrewAI与DeepSeek的邮件自动化实践

文章目录 引言理解 Flows&#xff08;工作流&#xff09;与 Crews&#xff08;协作组&#xff09;一、环境准备与工具安装1.1 Python环境搭建1.2 创建并激活虚拟环境1.3 安装核心依赖库&#xff08;crewai、litellm&#xff09; 二、本地DeepSeek R1大模型部署2.1 Ollama框架安…

[SQL] 事务的四大特性(ACID)

&#x1f384;事务的四大特性 以下就是事务的四大特性&#xff0c;简称ACID。 原子性&#x1f4e2;事务时不可分割的最小操作单元&#xff0c;要么全部成功&#xff0c;要么全部失败。一致性&#x1f4e2;事务完成后&#xff0c;必须使所有的数据都保持一致隔离性&#x1f4e2…

AI时代前端开发:自主学习能力的培养

在人工智能&#xff08;AI&#xff09;飞速发展的今天&#xff0c;技术迭代的速度如同脱缰的野马&#xff0c;对所有技术人员&#xff0c;特别是前端开发者&#xff0c;都提出了前所未有的挑战。新的框架、库、工具层出不穷&#xff0c;稍有松懈&#xff0c;就会被时代抛在身后…

【备赛】点亮LED

LED部分的原理图 led前面有锁存器&#xff0c;这是为了防止led会受到lcd的干扰&#xff08;lcd也需要用到这些引脚&#xff09;。 每次想要对led操作&#xff0c;就需要先打开锁存器&#xff0c;再执行操作&#xff0c;最后关闭锁存器。 这里需要注意的是&#xff0c;引脚配置…

Rocky8 源码安装 HAProxy

HAProxy 是一款开源的高性能 负载均衡器 和 反向代理 软件&#xff0c;专注于处理高并发流量分发&#xff0c;广泛应用于企业级架构中提升服务的可用性、扩展性和安全性。 一、HAProxy 简介 1.1.HAProxy 是什么&#xff1f; 本质&#xff1a; 基于 C 语言开发 的轻量级工具&a…

Javascript网页设计案例:通过PDFLib实现一款PDF分割工具,分割方式自定义-完整源代码,开箱即用

功能预览 一、工具简介 PDF 分割工具支持以下核心功能: 拖放或上传 PDF 文件:用户可以通过拖放或点击上传 PDF 文件。两种分割模式: 指定范围:用户可以指定起始页和结束页,提取特定范围的内容。固定间距:用户可以设置间隔页数(例如每 5 页分割一次),工具会自动完成分…

微信小程序调用火山方舟(字节跳动火山引擎)中的DeepSeek大模型

一、注册火山引擎账号&#xff0c;创建API Key和model&#xff08;接入点ID&#xff09; 1.注册并登陆火山引擎账号&#xff0c;网址为&#xff1a;https://console.volcengine.com/ 2.根据登陆后的页面提示进行实名认证&#xff0c;实名认证后才能创建API Keyt和创建接入点。…

Spring Boot 应用(官网文档解读)

Spring Boot 启动方式 SpringApplication.run(MyApplication.class, args); Spring Boot 故障分析器 在Spring Boot 项目启动发生错误的时候&#xff0c;我们通常可以看到上面的内容&#xff0c;即 APPLICATION FAILED TO START&#xff0c;以及后面的错误描述。这个功能是通过…

从单片机的启动说起一个单片机到点灯发生了什么下——使用GPIO点一个灯

目录 前言 HAL库对GPIO的抽象 核心分析&#xff1a;HAL_GPIO_Init 前言 我们终于到达了熟悉的地方&#xff0c;对GPIO的初始化。经过漫长的铺垫&#xff0c;我们终于历经千辛万苦&#xff0c;来到了这里。关于GPIO的八种模式等更加详细的细节&#xff0c;由于只是点个灯&am…