go结构体、指针、接口

type 关键字

package main
import (
	"fmt"
)
/*type关键字
1.定义结构体
2.定义接口
3.定义别名
*/
type MyInt int//类型定义;可以绑定方法
func main(){
	type Rint int//类型定义;通过已有类型自定义类型
	type eint = int//"="号是别名
	var e Rint
	var r MyInt = 5
	var t int = 6
	var w eint =9
	fmt.Printf("%T\n",r)	
	fmt.Printf("%T\r\n",e)
	fmt.Println(r+MyInt(t))
	fmt.Println(w+t)//在编译的时候,类型别名会被直接替换成对应类型(int)
	fmt.Printf("%T\n",w) //查看别名类型
	var a interface{} = "abc"
	switch a.(type){
	case string:
		fmt.Println("string")
	}

}

  结构体

package main
import (
	"fmt"
	//"go/types"
)
type Preson struct{
	name string
	age int
	address string
	height float32
}
func main(){
	//结构体初始化1
	p1 := Preson{
		name: "喜羊羊",
		age: 12,
		address: "羊村",
		height: 3.24,
	}
	//结构体初始化2
	p2:=Preson{ "美羊羊", 12, "羊村美容课",3.14}
	fmt.Println(p1)
	fmt.Println(p2)
	//结构体初始化3
	var p3 =Preson{name: "懒羊羊",age: 12,address: "睡觉觉",height: 2.65}
	fmt.Println(p3)
	var p4 []Preson
	p4=append(p4, p1,p2,p3)
	p5:=[]Preson{
		{
			name: "b4",
		},
		{
			age: 19,
		},

	}
	fmt.Println(p5)
	var p6 Preson
	p6.name="小灰灰"
	p6.age=5
	p6.address= "羊村"
	p6.height=3.14
	fmt.Println(p6)
	
}
/*
执行
{喜羊羊 12 羊村 3.24}
{美羊羊 12 羊村美容课 3.14}
{懒羊羊 12 睡觉觉 2.65}
[{b4 0  0} { 19  0}]
{小灰灰 5 羊村 3.14}
*/

  匿名结构体

package main

import (
	"fmt"
	//"structs"
	//"go/types"
)
type Preson struct{
	name string
	age int
	address string
	height float32
}
func main(){
	//匿名函数
	adder:=struct{
		province string
		city string
		address string
	}{
		province: "北京市",
		city: "通州",
		address: "XXX",

	}
	fmt.Println(adder.city)
	
}
/*
执行
通州
*/

  结构体嵌套

package main

import "fmt"

type Preson struct {
	name string
	age  int
}
type Preson1 struct {
	//结构体嵌套
	Preson
	score int
	name string//两个name 调度是默认这个优先级高
}

func main() {
	//嵌套结构体初始化
	s := Preson1{
		Preson{
		"灰太狼",
		20,
		},
		7,
		"小灰灰",
	}
	fmt.Println(s)
	fmt.Println(s.name)//覆盖作用显示小灰灰
	fmt.Println(s.Preson.name)//显示灰太狼

}
执行结果
{{灰太狼 20} 7 小灰灰}
小灰灰

  结构体定义方法

package main
import (
	"fmt"
)
type Preson struct{
	name string
	age int
}
//给结构体绑定方法;(s Preson)叫接收器,有两种形态。有值传递和引用传递
//有可能该方法中在修改结构体的值,或是结构体对象很大,数据较大
func (s Preson)Println(){
	fmt.Printf("name:%s,age:%d\n",s.name,s.age)
}
func main(){
	//初始化结构体,
	a:=Preson{"小灰灰",8}
	//调用结构体方法
	a.Println()
}

  指针初识

package main
import (
	"fmt"
)
type Person struct{
	name string
}
func changName(a *Person){
	a.name="慕课网"
}
func main(){
	p:=Person{
		name: "booy",
	}
	var pi *Person
	pi =&p 
	changName(pi)
	fmt.Printf("%p\n",pi)
	fmt.Println(pi.name)
	po:=&Person{
		"小杜",
	}
	//po=&p
	//第一个不同的点就出来了,直接使用变量一样使用指针,第二个点go语言限制了指针的运算,在C语言里你可以拿到一个指针进行加1,go语言中不支持,不能参加运算
	
	/*
	go的指针是阉割版,unsafe包里面,所以一般不会使用unsafe包,但是可以使用
	*/
	fmt.Println(po.name)

	
}

  指针初始化

package main
import (
	"fmt"
)
type Person struct{
	name string
}
func changName(a *Person){
	a.name="慕课网"
}
func main(){
	//var p1 *Person//结构体类型放入指针没有初始化不可以调用结构体字段panic: runtime error: invalid memory address or nil pointer dereference;需要初始化
	var p1 Person//结构体为赋值可以调用字段
	fmt.Println(p1.name)
	//结构体类型指针初始化方法1 
	ps := &Person{}
	//结构体类型指针初始化方法2
	var empt Person
	ps2 :=&empt
	//结构体类型指针初始化3
	//初始化两个关键字,map、channel、slice 初始化推荐make;指针初始化new函数,指针要初始化否则会出现nil pointer
	//map 必须使用make初始化
	var ps3 = new(Person)
	fmt.Println(ps.name)
	fmt.Println(ps2.name)
	fmt.Println(ps3.name)
}

  通过swap 交换指针的值(a,b=b,a//这样只是改变函数里ab变量的值,而不是变量值(地址值)指向的底层数据空间的值)

package main
import (
	"fmt"
)
type Person struct{
	name string
}
//接受者
func (p Person)SayHello(){

}
//通过指针交换2个值
func swap(a,b *int) {
	//a,b=b,a//这样只是改变函数里ab变量的值,而不是变量值(地址值)指向的底层数据空间的值
	*a,*b=*b,*a //交换两个值,*改底层的值
}
func main(){
	a:=10
	b:=20
	fmt.Println(a,b)
	swap(&a,&b)
	fmt.Println(a,b)
}
执行结果
10 20
20 10

  nil 的关键字理解;代表某一些数据的0值

package main

import (
	"fmt"
	//"net"
)
type Person struct{
	name string
	age int
}
func main(){
	/*
	不同类型的数据零值不一样
	bool false
	数值  0
	string ""
	pointer nil
	slice nil
	map nil
	channel、interface、function nil
	struct 默认值不是nil、默认值是具体字段的默认值
	*/
	p1 := Person{
		name: "美羊羊",
		age: 18,

	}
	p2 := Person{
		name: "美羊羊",
		age: 18,
	}
	if p1==p2{
		fmt.Println("yes")
	}
	//切片默认值
	var ps []Person
	if ps == nil{
		fmt.Println("ps为空")
	}
	var ps1 = make([]Person,0)//初始化默认值就不是nil了
	if ps1 == nil{
		fmt.Println("ps1为空")
	}else {
		fmt.Println("ps1不为空")
	}
	var m map[string]string//没有初始化为空
	if m == nil{
		fmt.Println("m为nil")
	}else {
		fmt.Println("m不为空")
	}
	var m1 = make(map[string]string,0)//初始化就不为空了
	if m1 == nil{
		fmt.Println("m1为nil")
	}else {
		fmt.Println("m1不为空")
	}	

	//有些类型默认值不可以和int类型比较
	// var a int
	// if a != nil{
	// 	fmt.Println()
	// }

}
执行结果
yes
ps为空
ps1不为空
m为nil
m1不为空

  接口

Go 语言中的 接口(interface) 是 Go 最重要的特性之一,也是实现多态的核心

接口定义

package main
import (
	"fmt"
)
//定义
type Duck interface{
	//方法
	Gage()
	Walk()
	Swimming()
}
type pskDuck struct{
}
//接口实现
func (pd *pskDuck)Gage(){
	fmt.Println("gaga")
}
func (pd *pskDuck)Walk(){
	fmt.Println("Walk")
}
func (pd *pskDuck)Swimming(){
	fmt.Println("momg ")
}
func main(){
	//go 语言的接口,鸭子类型;到处都是鸭子类型duck typing
	/*
	当看到一只鸟走起来像鸭子,游泳起来像鸭子、叫起来像鸭子、那么这只鸟就是鸭子
	动词,方法,看是否具备方法;鸭子的行为是强调的外部行为
	*/
	var a Duck = &pskDuck{}
	a.Gage()
	
}

  多接口实现

       一个结构体实现多个接口

package main
import (
	"fmt"
)
type MyWriter interface{
	Wiret(string) error

}
type MyCloser interface{
	Closer() error
}
type WriterCloser struct{}
func (r *WriterCloser) Wiret(string) error{
	fmt.Println("wite string")
	return nil
}
func (r *WriterCloser)Closer() error{
	fmt.Println("write string")
	return  nil
}
func main(){
	fmt.Println()
	var mw MyWriter = &WriterCloser{}
	var ms MyCloser = &WriterCloser{}
}

  一个接口多结构体实现

type Animal interface {
	Speak()
}
type Dog struct {
}

func (d Dog) Speak() {
	fmt.Println("汪汪汪")
}
type Cat struct {
}

func (c Cat) Speak() {
	fmt.Println("喵喵喵")
}
func MakeSound(a Animal) {
	a.Speak()
}
func main() {

	MakeSound(Dog{})
	MakeSound(Cat{})
	MakeSound(Bird{})

}
汪汪汪
喵喵喵
啾啾啾

  结构体嵌入接口

package main

import (
	"fmt"

	//"golang.org/x/tools/go/analysis/passes/hostport"
)
type MyWriter interface{
	Write(string) error

}
type MyCloser interface{
	Close() error
}
type writerCloser struct{
	MyWriter//这里必须匿名接口 一个接口实现
}
type fileWriter struct{}
func (f *fileWriter)Write(a string) error{
	fmt.Println("打开文件")
	return nil
}
type database struct{
	host string
}
func (f *database)Write(a string) error{
	fmt.Println("链接数据库")
	return nil
}
// func (wc *writerCloser)Write(a string) error{
// 	fmt.Println("write  string")
// 	return nil
// }
func (wc *writerCloser)Close() error{
	fmt.Println("Close")
	return  nil
}
func main(){
	var md MyWriter = &writerCloser{
	//	&fileWriter{},//注入接口实现
		&database{},
	}
	md.Write("a")

}
结果
//PS D:\golang\goproject\src\lnhgo> go run day03\demo09\main.go
//打开文件
PS D:\golang\goproject\src\lnhgo> go run day03\demo09\main.go
链接数据库

  内存分析

这段代码输出的是:
链接数据库
原因在于使用了 接口嵌入(embedding):
type writerCloser struct{
    MyWriter
}
而且把:
&database{}
赋值给了嵌入的 MyWriter:
var md MyWriter = &writerCloser{
    &database{},
}
实际上等价于:
wc := &writerCloser{
    MyWriter: &database{},
}
调用过程

当执行:
md.Write("a")
md 指向 writerCloser
writerCloser 本身没有 Write()
Go 会查找嵌入的 MyWriter
MyWriter 当前保存的是 *database
动态派发到:
func (f *database) Write(a string) error {
    fmt.Println("链接数据库")
    return nil
}
如果改成
var md MyWriter = &writerCloser{
    &fileWriter{},
}
writerCloser
│
└── MyWriter(interface)
        │
        ▼
    fileWriter{}
md.Write("a")
func (f *fileWriter) Write(a string) error {
    fmt.Println("打开文件")
    return nil
}
打开文件

为什么 writerCloser 没有定义 Write() 却实现了 MyWriter

这是 Go 的方法提升(Method Promotion)

type writerCloser struct{
    MyWriter
}
嵌入了一个接口,接口的方法会自动提升,相当于编译器帮你生成:
func (wc *writerCloser) Write(s string) error {
    return wc.MyWriter.Write(s)
}
所以:
var md MyWriter = &writerCloser{
    &database{},
}
是合法的。
如果再把这段取消注释:
func (wc *writerCloser)Write(a string) error{
    fmt.Println("write string")
    return nil
}
那么:
md.Write("a")
输出就会变成:
write string
因为 自己的方法优先级高于嵌入字段的方法。
这其实体现了 Go 面向对象最核心的一条规则:
外层结构体的方法 > 嵌入字段的方法 > 接口动态绑定到具体类型的方法
理解这一点,掌握了 Go 中组合(Composition)、接口、方法集和动态派发之间的关系。

  为什么嵌入接口就不会报错?

type writerCloser struct {
    MyWriter
}
那么 MyWriter 是匿名字段(嵌入字段)。

Go 会把嵌入字段的方法提升(Promote)到外层结构体。

相当于编译器偷偷帮你生成:

func (wc *writerCloser) Write(s string) error {
    return wc.MyWriter.Write(s)
}

var md MyWriter = &writerCloser{
    &database{},
}
编译器检查:

*writerCloser

的方法集时,会看到:

Write(string) error

于是:

*writerCloser implements MyWriter

因此不会报错。

  普通字段

type writerCloser struct {
    my MyWriter
}

结构:

writerCloser
    ↓
my
    ↓
database

调用必须:

wc.my.Write()

wc.Write() 不存在。

嵌入字段
type writerCloser struct {
    MyWriter
}

结构:

writerCloser
    ↓(方法提升)
MyWriter
    ↓
database

于是:

wc.Write()

实际上等价于:

wc.MyWriter.Write()

如果坚持使用普通字段,也可以自己实现转发:
type writerCloser struct {
    my MyWriter
}

func (wc *writerCloser) Write(s string) error {
    return wc.my.Write(s)
}

这样:

var md MyWriter = &writerCloser{
    my: &database{},
}

也不会报错。

所以,并不是「必须匿名接口」,而是:

接口的实现只看方法集(Method Set),不看成员变量。

普通字段:

type writerCloser struct {
    my MyWriter
}

方法集:

Close()

缺少:

Write()

因此不满足 MyWriter。

而匿名嵌入:

type writerCloser struct {
    MyWriter
}

方法集变成:

Write()   ← 来自嵌入字段的方法提升
Close()

因此自动满足 MyWriter。

这也是 Go 中组合(Composition)能够替代继承的核心机制之一。

  接口断言

package main
import "fmt"
func add(a,b int) int{
	return  a+b
}
func adds(a,b interface{}) int{
	ai,_:=a.(int)//断言
	bi,_:=b.(int)//断言
	return  ai+bi
}
func main(){
	a:=1
	b:=2
	fmt.Println(adds(a,b))
}
执行
3

  断言二

package main
import "fmt"
func add(a,b int) int{
	return  a+b
}
func adds(a,b interface{}) int{
	ai,_:=a.(int)//断言
	bi,_:=b.(int)//断言
	return  ai+bi
}
func addsq(a,b interface{}) int{
	ai,ok:=a.(int)
	if !ok{
		panic("not an int type")
	}
	bi,ok:=b.(int)
	if !ok{
		panic("not an int type")
	}
	return ai+bi
}
func main(){
	a:=1.3
	b:=2.5
	fmt.Println(addsq(a,b))
}


panic: not an int type

goroutine 1 [running]:
main.addsq(...)
        D:/golang/goproject/src/lnhgo/day03/demo10/main.go:14
main.main()
        D:/golang/goproject/src/lnhgo/day03/demo10/main.go:25 +0x25
exit status 2

  断言三

package main

import (
	"fmt"
	//"go/types"
)
func add(a,b int) int{
	return  a+b
}
func adds(a,b interface{}) int{
	ai,_:=a.(int)//断言
	bi,_:=b.(int)//断言
	return  ai+bi
}
func addsq(a,b interface{}) int{
	ai,ok:=a.(int)
	if !ok{
		panic("not an int type")
	}
	bi,ok:=b.(int)
	if !ok{
		panic("not an int type")
	}
	return ai+bi
}
func addsw(a,b interface{}) interface{}{
	switch a.(type){
	case int:
		ai,_:=a.(int)
		bi,_:=b.(int)
		return  ai+bi
	case float32:
		ai,_:=a.(float32)
		bi,_:=b.(float32)
		return  ai+bi
	case float64:
		ai,_:=a.(float64)
		bi,_:=b.(float64)
		return ai+bi
	case string:
		ai,_:=a.(string)
		bi,_:=b.(string)
		return ai+bi
	default:
		panic("ytr")
	}
}
func main(){
	a:=1.3
	b:=2.5
	fmt.Println(addsq(a,b))
}

  接口嵌套

package main

import "fmt"

type MyWriter interface {
	Reader(string)
}
type MyReader interface {
	Write(string)
}
type MyReadWriter interface {
	MyReader
	MyWriter
	ReadWrite()
}
type SreadWriter struct{}

func (s *SreadWriter)Write(string) {
	fmt.Println("implement me")
}
func (s *SreadWriter)Reader(string){
	fmt.Println("implement Reader")
}
func (s *SreadWriter)ReadWrite(){
	fmt.Println("ReadWrite()")
}
func main() { 
	var mrw MyReadWriter = &SreadWriter{}
	mrw.Reader("t")
 }
执行结果
PS D:\golang\goproject\src\lnhgo> go run day03\demo11\main.go
implement Reader

  接口遇到切片

package main

import "fmt"

func mPrint(datas ...interface{}) {
	for _, v := range datas {
		fmt.Println(v)
	}

}
func mPrint2(datas interface{}) {
	fmt.Println(datas)

}
type myinfo struct{}
func (mi *myinfo)Error() string{
	return "我不是错误"
}
func main() {
	var data = []string{
		"bobby", "tr", "te",
	}//如果date 是字符串切片类型就不可以
	

	var datai []interface{}
	for _,v:=range data{
		datai=append(datai,v)
	}
	mPrint(datai...)
	mPrint2(data)
}

  

 

 

 

 

posted @ 2026-06-20 11:06  烟雨楼台,行云流水  阅读(2)  评论(0)    收藏  举报