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)
}
草都可以从石头缝隙中长出来更可况你呢

浙公网安备 33010602011771号