golang 接口 interface 空接口 类型断言

接口

go 复制代码
package main

import "fmt"

/*
type 接口名称 interface{
     方法1(参数) 返回值
     方法2(参数) 返回值
}
*/

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

type Dog struct {
	name string
}

// Dog 实现 Animal 接口
func (d Dog) eat() {
	fmt.Println("小狗爱吃骨头") // 输出内容:小狗爱吃骨头
}

type Cat struct {
	name string
}

func (c Cat) eat() {
	fmt.Println("小猫爱吃鱼")
}

func main() {
	var d Dog
	// 普通的给struct添加方法
	d.eat() // 输出:小狗爱吃骨头

	var a Animal
	a = d
	a.eat() // 输出:小狗爱吃骨头
}

// 最终程序运行输出:
// 小狗爱吃骨头
// 小狗爱吃骨头

空接口

go 复制代码
// 空接口,可以存任意类型的数据
package main

import "fmt"

type A interface{}

func main() {
     var a A
     var a1 interface{}
     a = "Hello world"
     a1 = "Hello world"
     fmt.Println(a, a1)

     a = 20
     a1 = 20
     fmt.Println(a, a1)
}

空接口在map和slice的应用

css 复制代码
package main

import "fmt"

func main(){
     // map类型,值可以是任意类型
     var a = map[string]interface{}{}
     a["name"] = "张三"
     a["age"] = 18
     a["isGender"] = false

     fmt.Println(a)


     var b = []interface{}{}
     b = append(b, 100)
     b = append(b, "hello world")
     b = append(b, false)
     fmt.Println(b)
}

值和指针

go 复制代码
// 值类型和指针类型
package main

import "fmt"

type Animal interface {
	Eat()
}

type Dog struct {
	name string
}

// ✅ 值接收者:传的是【拷贝】,方法内修改不会影响原变量
func (d Dog) Eat() {
	d.name += "_rename"
	fmt.Printf("%v狗吃骨头\n", d.name)
}

type Cat struct {
	name string
}

// ✅ 指针接收者:传的是【地址/引用】,修改会直接改原变量
func (c *Cat) Eat() {
	c.name += "_rename"
	fmt.Printf("%v猫吃鱼\n", c.name)
}

func main() {
	// ========== Dog:值接收者(拷贝) ==========
	var d Dog
	d.name = "旺财"
	var d1 = &Dog{"旺财1"}

	d.Eat()   // 输出:旺财_rename狗吃骨头
	d.Eat()   // 输出:旺财_rename狗吃骨头
	// 📝 笔记:两次都是【拷贝】,原变量 name 永远是 "旺财",不会累加

	d1.Eat()  // 输出:旺财1_rename狗吃骨头
	d1.Eat()  // 输出:旺财1_rename狗吃骨头
	// 📝 笔记:指针调用值接收者,依然是【拷贝】,不会修改原变量

	// ========== Cat:指针接收者(引用) ==========
	var c Cat
	c.name = "凯特"
	var c1 = &Cat{"凯特1"}

	c.Eat()   // 输出:凯特_rename猫吃鱼
	c.Eat()   // 输出:凯特_rename_rename猫吃鱼
	// 📝 笔记:Go自动取地址 &c,调用指针方法
	// 📝 【修改的是原变量】,所以 name 会一直累加

	c1.Eat()  // 输出:凯特1_rename猫吃鱼
	c1.Eat()  // 输出:凯特1_rename_rename猫吃鱼
	// 📝 笔记:指针调用指针方法
	// 📝 【修改原变量】,持续累加
}

类型断言

r 复制代码
x.(T)
go 复制代码
package main

import "fmt"

// ==============================
// 函数作用:接收任意类型,判断并返回类型名称
// instance interface{}:空接口,可以接收【任意类型】的值
// ==============================
func getInstanceType(instance interface{}) string {
     // ==============================
     // 【类型选择语法】instance.(type)
     // 🔥 规则:只能用在 switch 语句中,不能单独拿出来用!
     // 作用:获取 instance 内部存储的【真实类型】
     // ==============================
     switch instance.(type) {
     case int:
        fmt.Println("int")
        return "int"
     case string:
        fmt.Println("string")
        return "string"
     default:
        fmt.Println("Unsupport")
        return "nil"
     }
}

func main() {
     // 调用函数,传入 int 和 string
     getInstanceType(100)
     getInstanceType("hello")

     // ==============================
     // ❌ 语法错误!
     // 100 是 int 字面量,不是接口
     // (type) 只能在 switch 里使用
     // var res = 100.(type)
     // ==============================

     // ==============================
     // 声明空接口变量 a
     // 空接口可以存储【任意类型】的值
     // ==============================
     var a interface{}
     a = "Hello world" // 存储 string 类型

     // ==============================
     // 【类型断言】语法:变量.(目标类型)
     // 🔥 必须满足:左边的变量 一定是【接口类型】
     // content:断言成功后得到的【真实值】
     // ok:断言是否成功(bool)
     // ==============================
     if content, ok := a.(int); ok {
        fmt.Println("int类型", content)
     } else if content, ok := a.(string); ok {
        fmt.Println("string类型", content)
     }

     // ==============================
     // ❌ 错误示范!
     // t1 是 int 类型(普通变量),不是接口
     // 类型断言【左边必须是接口变量】
     // var t1 int = 10
     // if content,ok := t1.(int); ok {
     //  fmt.Println(content)
     // }
     // ==============================

     // ==============================
     // ✅ 正确示范!
     // t2 是 interface{} 空接口变量
     // 可以安全使用类型断言
     // ==============================
     var t2 interface{} = 20
     if content, ok := t2.(int); ok {
        fmt.Println(content) // 输出 20
     }
}
相关推荐
武子康2 小时前
Java-07 深入浅出 MyBatis数据库一对多关系模型实战:表结构设计与查询实现
java·后端
花椒技术3 小时前
企业内部 Agent 落地复盘:Gateway、Skill 和二次确认如何串起受控业务执行
后端·agent·ai编程
我是一颗柠檬5 小时前
【MySQL全面教学】MySQL事务与ACID Day9(2026年)
数据库·后端·mysql
枕星而眠5 小时前
数据结构八大排序详解(一):四大简单排序
c语言·数据结构·c++·后端
IT_陈寒5 小时前
React useEffect闭包陷阱差点把我整失业了
前端·人工智能·后端
苍何6 小时前
爆肝两周,我把 Codex 最全实战指南开源了
后端
bug菌6 小时前
【SpringBoot 3.x 第254节】夯爆了,数据库访问性能优化实战详解!
数据库·spring boot·后端
Rust研习社6 小时前
从碎片化到标准化:cargo-bp 如何重构 Rust 开发逻辑
后端·rust·编程语言
锋行天下6 小时前
一句mysql复杂查询搞崩一个壮汉
后端·mysql·go
不肯过江东丶6 小时前
大聪明教你学Java | Spring AI Lab:一个让你 3 分钟接入 AI 对话能力的 Spring Boot 工具箱
spring boot·后端