others linux服务器运维 django3 监控 k8s golang 数据库 大数据 前端 devops 理论基础 java oracle 运维日志

golang 语言基础四

访问量:1105 创建时间:2021-02-03

Golang time包、日期函数

主要内容:time包 ,time.Now()获取当前时间,now.Format格式化输出日期字符串,time.Now()获取当前的时间戳,时间戳转换为日期字符串(年-月-日 时:分:秒),now.Format 把时间戳格式化成日期,日期字符串转换成时间戳,时间间隔,时间操作函数,定时器

package main

import (
    "fmt"
    "time"
)

func main() {
    //打印当前日期
    timeObj := time.Now()
    fmt.Println(timeObj) //2021-02-03 15:31:56.449056 +0800 CST m=+0.003960801
    year := timeObj.Year()
    month := timeObj.Month()
    day := timeObj.Day()
    hour := timeObj.Hour()
    minute := timeObj.Minute()
    second := timeObj.Second()
    fmt.Println(year, month, day, hour, minute, second)                                 //2021 February 3 15 36 33
    fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second) //2021-2-3 15:36:33
    //02d表示整数不够2位,用0补充
    //Format方法,2006表示年 01表示月 02 日 03 时(12小时制) 15 24小时制 04 分 05 秒
    str := timeObj.Format("2006-01-02 03:04:05") //2021-02-03 03:40:11
    fmt.Println(str)
    str1 := timeObj.Format("2006-01-02 15:04:05") //2021-02-03 15:42:21
    fmt.Println(str1)
    //获取时间戳
    timestamp := timeObj.Unix()
    fmt.Println(timestamp)
    //纳秒时间戳
    unixNatime := timeObj.UnixNano()
    fmt.Println(unixNatime)
    //时间戳转换日期字符串
    var unixTime int64 = 1577886676
    timeObj1 := time.Unix(unixTime, 0) //Unix方法参数int64,第二个参数是纳秒参数。
    fmt.Println(timeObj1.Format("2006-01-02 15:04:05"))
    //日期字符串转换成时间戳
    var str2 = "2021-01-01 12:01:20"
    var tmp = "2006-01-02 15:04:05"
    timeObj2, _ := time.ParseInLocation(tmp, str2, time.Local)
    fmt.Println(timeObj2) //2021-01-01 12:01:20 +0800 CST
    fmt.Println(timeObj2.Unix())
    /* time包中的时间间隔类型常量
    const (
        Nanosecond Duration = 1
        Microsecond =1000 * Nanosecond
        Millisecond = 1000*Microsecond
        Second = 1000*Millisecond
        Mintue = 60* Second
        Hour = 60 * Mintue
    )

    */
    fmt.Println(time.Millisecond) //1毫秒
    fmt.Println(time.Second)      //1秒
    //时间函数 Add(time.Hour)
    // Sub() 求2个时间差
    // Equal()判断时间相等
    var timeNow = time.Now()
    fmt.Println(timeNow)
    timeNow1 := timeNow.Add(time.Hour)
    fmt.Println(timeNow1)

    //golang定时器 time.NewTicker(时间间隔) 或者  time.Sleep(time.Second)
    ticker := time.NewTicker(time.Second) //表示每隔1秒执行一次
    n := 5
    for t := range ticker.C {
        n--
        if n == 0 {
            ticker.Stop() //终止定时器
            break
        }
        fmt.Println(t)
    }

    fmt.Println("aaa")
    time.Sleep(time.Second)
    fmt.Println("aaa")
    time.Sleep(time.Second * 5)
    fmt.Println("aaa")
    time.Sleep(time.Second)
    fmt.Println("aaa")
    for {
        time.Sleep(time.Second)
        fmt.Println("for sleep....")
    }
}

Golang指针 、make new方法分配内存

指针:也是变量,他存储的数据不是普通的值而是另一个变量的内存地址。

package main
import (
    "fmt"
)

func main() {
    // 在计算机底层a变量对应一个内存地址
    var a int = 10
    var p *int = &a                    //指针变量  p的类型 *int
    fmt.Printf("%v %T %p\n", a, a, &a) //值,类型, 地址
    fmt.Printf("%v %T %p\n", p, p, &p) //值,类型, 地址
}

指针地址和指针类型,每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行取地址操作。Go语言中值类型(int,float,string,array,struct)都有对应的指针类型,如int ,int64, *string等。

取变量指针的语法: ptr := &v v表示被取地址的变量 ,类型为T ptr用于接收地址的变量,ptr类型就为T,称作T的指针类型,代表指针。

指针取值

package main
import "fmt"

func main() {
    // 在计算机底层a变量对应一个内存地址
    var a int = 10
    var p *int = &a                    //指针变量  p的类型 *int
    fmt.Printf("%v %T %p\n", a, a, &a) //值,类型, 地址
    fmt.Printf("%v %T %p\n", p, p, &p) //值,类型, 地址
    fmt.Println(*p)    //取指针的值                // p的值-指向的内存中存放的值
}

指针传值示例

package main
import "fmt"

func main() {
    // 在计算机底层a变量对应一个内存地址
    var a int = 10
    var p *int = &a                    //指针变量  p的类型 *int
    fmt.Printf("%v %T %p\n", a, a, &a) //值,类型, 地址
    fmt.Printf("%v %T %p\n", p, p, &p) //值,类型, 地址
    fmt.Println(*p)                    //取指针的值                // p的值-指向的内存中存放的值
    *p = 30
    fmt.Println(a)
}

案例2:

package main
import "fmt"

func fn1(x int) {
    x = 10
}

func fn2(x *int) {
    *x = 40
}

func main() {
    var a int = 5
    fn1(a)
    fmt.Println(a) //5
    fn2(&a)
    fmt.Println(a) //40
}

new和make函数,new()方法给指针变量分配内存空间;make()函数也用于分配内存空间,它只用于slice,map,channel的内存创建,返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三个类型就是引用类型,所以没必要返回他们的指针了。

package main
import "fmt"

func main() {
    //引用类型的数据必须分配内存空间后,才能初始化(map、切片都需要分配内存)
    //var userinfo map[string]string
    //userinfo["username"] = "张三"
    //fmt.Println(userinfo)
    //下面用make函数会分配内存空间,所以可以赋值成功
    var userinfo = make(map[string]string)
    userinfo["username"] = "张三"
    fmt.Println(userinfo)

    var a = make([]int, 4, 4)
    a[0] = 1
    fmt.Println(a)
    //下面错误的操作
    //var a *int
    //*a=100
    //fmt.Print(*a)
    a1 := new(int) // new()创建指针类型
    *a1 = 100
    fmt.Println(a1, *a1)
}

结构体

结构体: golang中没有“类”的概念,golang中的结构体和其他语言的类相似,和其他语言的类相比,golang结构体具有更高的扩展性和灵活性。struct结构体 可以自定义数据类型,可以封装多个基本数据类型。

package main
import "fmt"

// 自定义类型
type myInt int
//type myFn func(int, int) int

//类型别名
type myfloat = float64

func main() {
    var a myInt = 10
    fmt.Printf("%v %T\n", a, a) //10 main.myInt
    var b myfloat = 11.1
    fmt.Printf("%v %T\n", b, b) //11.1 float64
}

使用type和struct 关键字定义结构体: 类型名在同一个包内不能重复,结构体中字段名必须为宜,字段类型表示结构体的具体类型。

type 类型名 struct { 字段名 字段类型 字段名 字段类型 ... }

package main
import "fmt"

//定义结构体
type Person struct {
    name string
    age  int
    sex  string
}

func main() {
    var p1 Person //实例化person结构体
    p1.name = "张三"
    p1.sex = "男"
    p1.age = 20
    fmt.Printf("%v %T\n", p1, p1)  //{张三 20 男} main.Person
    fmt.Printf("%#v %T\n", p1, p1) //main.Person{name:"张三", age:20, sex:"男"} main.Person
    var p2 = new(Person)           //p2指针类型
    p2.name = "李四"
    p2.age = 22
    p2.sex = "男"
    (*p2).name = "王五"
    fmt.Printf("%#v %T\n", p2, p2) //&main.Person{name:"李四", age:22, sex:"男"} *main.Person

    p3 := &Person{} //p3指针类型
    p3.age = 11
    p3.sex = "男"
    p3.name = "赵四"
    fmt.Printf("%#v %T\n", p3, p3) //&main.Person{name:"赵四", age:11, sex:"男"} *main.Person
    p4 := Person{
        name: "zs",
        age:  23,
        sex:  "男",
    }
    fmt.Printf("%#v %T\n", p4, p4) //main.Person{name:"zs", age:23, sex:"男"} main.Person
    p5 := &Person{
        name: "zs",
        age:  23,
        sex:  "男",
    }
    fmt.Printf("%#v %T\n", p5, p5) //&main.Person{name:"zs", age:23, sex:"男"} *main.Person
    p6 := &Person{
        name: "aaa",
    }
    fmt.Printf("%#v %T\n", p6, p6) //&main.Person{name:"aaa", age:0, sex:""} *main.Person
    p7 := &Person{                 //不指定key
        "zs",
        23,
        "男",
    }
    fmt.Printf("%#v %T\n", p7, p7)
}

结构体、结构体方法、自定义类型增加方法

go没有类的概念但可以给类型(结构体,自定义类型)定义方法。所谓方法就是定义了接收者的函数。接收者的概念类似于其他语言的this或者self.

方法定义格式如下:

func (接收者变量 接收者类型) 方法名(参数列表) (返回参数){ 函数体 }

package main
import "fmt"

//定义结构体
type Person struct {
    Name   string
    Age    int
    Sex    string
    height int
}

//结构体方法
func (p Person) PrintInfo() {
    fmt.Printf("姓名: %v, 年龄: %v 。\n", p.Name, p.Age)
}
func (p *Person) SetInfo(name string, age int) {
    p.Name = name
    p.Age = age
}

//自定义类型增加方法,非本地类型不能自定义方法,不能给别的包的类型定义方法。
type myInt int

func (m myInt) SayHello() {
    fmt.Println("hello, I am int.")
}

func main() {
    var p1 = Person{
        Name:   "张三",
        Age:    22,
        Sex:    "男",
        height: 170,
    }
    p1.PrintInfo() //姓名: 张三, 年龄: 22 。
    p1.SetInfo("李四", 30)
    p1.PrintInfo() //姓名: 李四, 年龄: 30 。
    var m myInt = 10
    m.SayHello()
}

结构体、嵌套结构体、结构体继承

package main
import "fmt"

//匿名字段,类型不能重复
type Person struct {
    string
    int
}

func main() {
    var p1 = Person{
        "张三",
        22,
    }
    fmt.Printf("%v %T\n", p1, p1)
}

嵌套结构体 & 嵌套匿名结构体

package main
import "fmt"

type Person struct {
    Name  string
    Age   int
    Hobby []string
    map1  map[string]string
}

//结构体嵌套

type User struct {
    Username string
    Password string
    Sex      string
    Age      int
    //Address  Address // 表示user结构体嵌套address
    Address //嵌套匿名结构体
}

type Address struct {
    Name  string
    Phone string
    City  string
}

func main() {
    var p1 Person
    p1.Name = "张三"
    p1.Age = 20
    p1.Hobby = make([]string, 6, 6)
    p1.Hobby[0] = "吃饭"
    p1.Hobby[1] = "睡觉"
    p1.map1 = make(map[string]string)
    p1.map1["address"] = "长安"
    p1.map1["phone"] = "11112323"
    fmt.Printf("%#v %T\n", p1, p1) //main.Person{Name:"张三", Age:20, Hobby:[]string{"吃饭", "睡觉", "", "", "", ""}, map1:map[string]string{"address":"长安", "phone":"11112323"}} main.Person
    var u User
    u.Username = "abc"
    u.Password = "123456"
    u.Address.Name = "张三"
    u.Address.Phone = "138000010001"
    u.Address.City = "beijing"
    u.City = "上海"          // 先查找结构体字段,在查找匿名嵌套结构体
    fmt.Printf("%#v\n", u) //main.User{Username:"abc", Password:"123456", Sex:"", Age:0, Address:main.Address{Name:"张三", Phone:"138000010001", City:"beijing"}}
}

结构体的继承(通过嵌套实现)

package main

import "fmt"

//父结构体
type Animal struct {
    name string
}

func (a Animal) run() {
    fmt.Printf("%v run...\n", a.name)
}

//子结构体
type Dog struct {
    Age    int
    Animal //继承 嵌套
}

func (d Dog) wang() {
    fmt.Printf("%v 在旺旺...\n", d.name)
}

func main() {
    var d = Dog{
        Age: 20,
        Animal: Animal{
            name: "hashiqi",
        },
    }
    d.run()//继承
    d.wang()
}

嵌套指针

package main

import "fmt"

//父结构体
type Animal struct {
    name string
}

func (a Animal) run() {
    fmt.Printf("%v run...\n", a.name)
}

//子结构体
type Dog struct {
    Age     int
    *Animal //指针
}

func (d Dog) wang() {
    fmt.Printf("%v 在旺旺...\n", d.name)
}

func main() {
    var d = Dog{
        Age: 20,
        Animal: &Animal{
            name: "hashiqi",
        },
    }
    d.run()
    d.wang()
}

结构体和Json相互转换 序列化 反序列化

关于JSON(javaScript object notation)数据,是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于及其解析和生成。RESTfull api接口返回的数据都是json数据。

结构体与JSON序列化

package main

import (
    "encoding/json"
    "fmt"
)

type Student struct {
    //私有地址不能被json包访问,所以,字段首字母大写
    Id     int
    Gender string
    Name   string
    Sno    string
}

func main() {
    var s1 = Student{
        Id:     12,
        Gender: "男",
        Name:   "李四",
        Sno:    "s0001",
    }
    fmt.Printf("%#v\n", s1)
    jsonByte, _ := json.Marshal(s1) // 返回Byte类型切片
    jsonStr := string(jsonByte)     // 转换Byte到string
    fmt.Printf("%v\n", jsonStr)     //{"Id":12,"Gender":"男","Name":"李四","Sno":"s0001"}
    //定义json字符串
    var str = `{"Id":12,"Gender":"男","Name":"刘能","Sno":"s0001"}`
    var s2 Student
    err := json.Unmarshal([]byte(str), &s2)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%#v\n", s2)//main.Student{Id:12, Gender:"男", Name:"刘能", Sno:"s0001"}
}

结构体标签Tag

type Student struct {
    //私有地址不能被json包访问,所以,字段首字母大写
    Id     int `json:"id"` // 通过指定tag实现json序列化字段时的key
    Gender string `json:"gender"` 
    Name   string `json:"name"` 
    Sno    string `json:"sno"` 
}

嵌套结构体和JSON序列化反序列化

package main
import (
    "encoding/json"
    "fmt"
)

type Student struct {
    Id     int
    Gender string
    Name   string
}

type Class struct {
    Title   string
    Student []Student
}

func main() {
    c := Class{
        Title:   "001班",
        Student: make([]Student, 0, 200),
    }
    //给student增加数据
    for i := 1; i <= 10; i++ {
        s := Student{
            Id:     i,
            Gender: "男",
            Name:   fmt.Sprintf("stu_%v", i),
        }
        c.Student = append(c.Student, s)
    }
    fmt.Println(c)

    strByte, err := json.Marshal(c)
    if err != nil {
        fmt.Println(err)
    }
    strJson := string(strByte)
    fmt.Println(strJson)
    //转换json到结构体
    str1 := `{"Title":"002班","Student":[{"Id":1,"Gender":"男","Name":"stu_1"},{"Id":2,"Gender":"男","Name":"stu_2"},{"Id":3,"Gender":"男","Name":"stu_3"},{"Id":4,"Gender":"男","Name":"stu_4"},{"Id":5,"Gender":"男","Name":"stu_5"},{"Id":6,"Gender":"男","Name":"stu_6"},{"Id":7,"Gender":"男","Name":"stu_7"},{"Id":8,"Gender":"男","Name":"stu_8"},{"Id":9,"Gender":"男","Name":"stu_9"},{"Id":10,"Gender":"男","Name":"stu_10"}]}`
    var c1 = &Class{}
    err1 := json.Unmarshal([]byte(str1), c1)
    if err1 != nil {
        fmt.Println(err1)
    } else {
        fmt.Printf("%#v", c1)
    }
}

go mod 以及 Golang 包详解

包种类 说明
系统内置包 Golang语言给我们提供的包,引入后可以直接使用,如fmt\strconv\strings\sort\errors\time\encoding/json\ os\ io等
自定义包 开发者自己写的包
第三方包 属于自定义包的一种,需要下载安装到本地才可以使用,如前面介绍的"github.com/shopspring/decimal"包解决float精度丢失问题

go mod init 初始化项目,在实际项目开发中,我们首先要在项目目录中使用go mod命令生成一个go.mod文件管理我们项目的依赖。

Microsoft Windows [版本 10.0.18363.1316]
(c) 2019 Microsoft Corporation。保留所有权利。

C:\Users\Administrator\Desktop\tardis>go mod init tardis
go: creating new go.mod: module tardis

C:\Users\Administrator\Desktop\tardis>

在vscode中导入项目,在vscode中文件==>打开文件夹,将刚才的目录打开

Golang中自定义包,创建目录calc与cala.go

使用自定义的包,在main.go文件中import导入

package main

import (
    "fmt"
    "tardis/calc"
)

func main() {
    sum := calc.Add(1, 2)
    fmt.Println(sum) //3
}

包(package)是多个Go源码的集合,一个包可以简单理解为一个存放在多个.go文件的文件夹。该文件夹下面的所有go文件都要在代码的第一行添加 的代码声明 ,声明该文件归属的包。

注意事项: - 一个文件夹下面直接包含的文件只能归属一个package.同一个package文件不能在多个目录下。 - 包名可以不和文件夹的名字一样,包名不能包含 - 符号 - 包名为main的包为应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含main包的源代码不会得到可执行文件。 - package 包名 ,必须在文件最顶一行。 - 包引入可以定义别名。如果包名很长,或者名字和其他包有冲突 - 匿名导入一个包: import _ "包的路径"

golang的init()函数,init()函数在main()之前执行。 下图每个包都有init()函数,执行过程如下图:

main.go

package main

import (
    "fmt"
    "tardis/calc"
)

func init() {
    fmt.Println("main init...")
}

func main() {
    sum := calc.Add(1, 2)
    fmt.Println(sum) //3
}

calc.go

package calc // 建议自定义的包名calc和自定义的上层目录名calc统一起来(可以不同)
import "fmt"

func init() {
    fmt.Println("calc init ...")
}

func Add(x, y int) int { //首字母大写--公有方法
    return x + y
}

func Sub(x, y int) int { //如果首字母小写,方法只能在当前包里面使用
    return x - y
}

golang使用第三方包

第三方包可以在https://pkg.go.dev/ 查找常见的golang第三方包。

安装第三方包

方法一:go get 包名称 (全局)

go get gethub.com/shopspring/decimal

方法二: go mod download (全局)

方法三: go mod vendor 将依赖的包复制到当前项目目录的vendor目录下

接口介绍、接口定义、实现接口(一)

接口作用比喻:

例如我定义了一个接口,但是我在继承这个接口的类中还要写接口的实现方法,那我不如直接就在这个类中写实现方法岂不是更便捷,还省去了定义接口?
接口就是个招牌。比如说你今年放假出去杭州旅游,玩了一上午,你也有点饿了,突然看到前面有个店子,上面挂着KFC,然后你就知道今天中饭有着落了。
KFC就是接口,我们看到了这个接口,就知道这个店会卖炸鸡腿(实现接口)。
那么为神马我们要去定义一个接口涅,这个店可以直接卖炸鸡腿啊(直接写实现方法),是的,这个店可以直接卖炸鸡腿,但没有挂KFC的招牌,我们就不能直接简单粗暴的冲进去叫服务员给两个炸鸡腿了。要么,我们就要进去问,你这里卖不卖炸鸡腿啊,卖不卖汉堡啊,卖不卖圣代啊(这就是反射)。
很显然,这样一家家的问实在是非常麻烦(反射性能很差)。
要么,我们就要记住,中山路108号卖炸鸡,黄山路45号卖炸鸡(硬编码),很显然这样我们要记住的很多很多东西(代码量剧增),而且,如果有新的店卖炸鸡腿,我们也不可能知道(不利于扩展)。

作者:Ivony
链接:https://www.zhihu.com/question/20111251/answer/16585393
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Golang接口的定义: - 在Golang中接口(interface)是一种类型,一种抽象的类型。接口是一组函数method的集合,Golang中的接口不能包含任何变量。 - 在Golang中接口中的所有方法都没有方法体,接口定义了一个对象的行为规范,只定义规范不实现。接口体现了程序设计的多态和高内聚低耦合的思想。 - Golang中接口也是一种数据类型,不需要显示实现。只需要在一个变量含有接口类型中的所有方法,那么这个变量就实现了这个接口。

格式 - 接口名: 使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如果写操作的接口叫做Writer,有字符串功能的接口叫做Stringer等。 - 方法名: 当方法名首字母是大写且这个接口类型名首字母也是大写,这个方法可以被接口所在的包(package)之外的代码访问。 - 参数列表、返回值列表: 参数列表和返回值列表中的参数变量名可以省略。

tyep 接口名 interface {
    方法名1 (参数列表1) 返回值列表1
    方法名2 (参数列表2) 返回值列表2
    ...
}

案例:

package main

import (
    "fmt"
)

type Usber interface {
    start()
    stop()
    //stop(string, string) string
}

// 如果接口里面有方法,必须要通过结构体或者自定义类型来实现这个接口
type Phone struct {
    Name string
}
// 手机要实现usb接口,必须要实现usb接口中的所有方法
func (p Phone) start() {
    fmt.Println(p.Name, "启动")
}
func (p Phone) stop() {
    fmt.Println(p.Name, "关机")
}

type Camera struct {
}
func (c Camera) start() {
    fmt.Println("相机启动")
}
func (c Camera) stop() {
    fmt.Println("相机关机")
}

//电脑
type Computer struct {
}
func (c Computer) work(usb Usber) {
    usb.start()
    usb.stop()
}

func main() {
    p := Phone{
        Name: "华为手机",
    }
    //p.start()
    var p1 Usber //golang中接口就是一种数据类型
    p1 = p       //表示手机实现usber接口
    //p1.stop()
    c := Camera{}
    var c1 Usber = c //表示相机实现了usber接口
    c1.start()
    var computer = Computer{}
    computer.work(p)
    computer.work(c)
    computer.work(p1)
}

空接口、类型断言(二)

空接口

package main

import "fmt"

type A interface{}

//空接口作为函数参数,show函数参数接受任意类型参数
func show(a interface{}) {
    fmt.Printf("type:%T value:%v\n", a, a)
}

func main() {
    var a A
    var str = "你好"
    a = str //让字符串实现A接口
    fmt.Printf("值:%v 类型:%T\n", a, a)
    var num = 20
    a = num //让int类型实现A接口
    fmt.Printf("值:%v 类型:%T\n", a, a)

    // golang中空接口也可以直接当做类型来使用
    var b interface{}
    b = 20
    fmt.Printf("值:%v 类型:%T\n", b, b)
    b = "你好"
    fmt.Printf("值:%v 类型:%T\n", b, b)
    b = true
    fmt.Printf("值:%v 类型:%T\n", b, b)
    //空接口参数的函数
    show(20)
    show("你好")
    slice := []int{1, 2, 3, 4}
    show(slice)
    //map老定义方法
    var m1 = make(map[string]string)
    m1["username"] = "张三"
    m1["age"] = "20"
    //空接口定义map,值可以是任意类型
    var m2 = make(map[string]interface{})
    m2["username"] = "张三"
    m2["age"] = 20
    //切片老方法定义
    var s1 = []int{1, 2, 3, 44}
    fmt.Println(s1) //[1 2 3 44]
    //空接口定义切片
    var s2 = []interface{}{1, 2, "你好", 1.2, true}
    fmt.Println(s2) //[1 2 你好 1.2 true]
}

类型断言

一个接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的,这两部分分别称为接口的动态类型和动态值。如果想要判断空接口中值的类型,可以使用类型断言,语法格式:x.(T), X表示类型为interface{}的变量,T表示断言x可能是的类型。该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。

简单案例:

package main

import "fmt"

//定义一个方法,根据不同的类型实现不同的操作
func myPrint(x interface{}) {
    if _, ok := x.(string); ok {
        fmt.Println("string")
    } else if _, ok := x.(int); ok {
        fmt.Println("int")
    } else if _, ok := x.(bool); ok {
        fmt.Println("bool")
    }
}

//x.(type)判断一个变量的类型,这个语句只能用在swaitch语句中
func myPrint1(x interface{}) {
    switch x.(type) {
    case int:
        fmt.Println("int")
    case string:
        fmt.Println("string")
    case bool:
        fmt.Println("bool")
    default:
        fmt.Println("没有找到类型")
    }
}

//类型断言
func main() {
    var a interface{}
    //a = "你好"
    a = 11
    v, ok := a.(string)
    if ok {
        fmt.Println("a是string", v)
    } else {
        fmt.Println("断言失败")
    }
    myPrint("aaa")
    myPrint(111)
    myPrint1(true)
    myPrint1("aaa")
}

结构体类型断言:

package main
import "fmt"

type Usber interface {
    start()
    stop()
}

// 手机
type Phone struct {
    Name string
}

func (p Phone) start() {
    fmt.Println(p.Name, "启动")
}
func (p Phone) stop() {
    fmt.Println(p.Name, "关机")
}

//相机
type Camera struct {
}

func (c Camera) start() {
    fmt.Println("相机启动")
}
func (c Camera) stop() {
    fmt.Println("相机关机")
}

//电脑
type Computer struct {
}

func (c Computer) work(usb Usber) {
    //要判断usb的类型
    if _, ok := usb.(Phone); ok {
        usb.start()
    } else {
        usb.stop()
    }
}

func main() {
    var computer = Computer{}
    var phone = Phone{
        Name: "小米",
    }
    var camera = Camera{}
    computer.work(phone)  //小米 启动
    computer.work(camera) //相机关机
}

结构体实现多接口、接口嵌套、结构体指针接收者实现接口(三)

结构体值接收者和指针接收者实现接口的区别

结构体值类型接收者案例:

package main
import "fmt"

/*
    结构体值接收者实现接口:
    值接收者:如果结构体中的方法时值接收者,那么实例化后的结构体值类型和结构体指针类型都可以赋值给接口变量
*/

type Usber interface {
    start()
    stop()
}

// 手机
type Phone struct {
    Name string
}

func (p Phone) start() { //值接收者
    fmt.Println(p.Name, "启动")
}
func (p Phone) stop() {
    fmt.Println(p.Name, "关机")
}

func main() {
    //结构体值接收者实例化后的结构体值类型和结构体指针类型都可以赋值给接口变量
    var p = Phone{
        Name: "小米手机",
    }
    var p1 Usber = p //表示让phone实例化usb接口
    p1.start()
    var p2 = &Phone{
        Name: "苹果手机",
    }
    var p3 Usber = p2
    p3.start()
}

指针接收者实现接口案例(指针类型接收者只能用指针类型实例化):

package main

import "fmt"

type Usber interface {
    start()
    stop()
}

type Phone struct {
    Name string
}

func (p *Phone) start() { //值接收者
    fmt.Println(p.Name, "启动")
}
func (p *Phone) stop() {
    fmt.Println(p.Name, "关机")
}

func main() {
    var p2 = &Phone{
        Name: "苹果手机",
    }
    var p3 Usber = p2
    p3.start()
}

案例二:

package main

import "fmt"

//定义一个Animal的接口,Animal中定义两个方法,分别是SetName和GetName,分别让Dog结构体和Cat结构体实现这个方法
type Animal interface {
    SetName(string)
    GetName() string
}

type Dog struct {
    Name string
}

func (d *Dog) SetName(name string) {
    d.Name = name
}
func (d Dog) GetName() string {
    return d.Name
}

type Cat struct {
    Name string
}

func (c *Cat) SetName(name string) {
    c.Name = name
}
func (c Cat) GetName() string {
    return c.Name
}

func main() {
    //Dog实现animal接口
    d := &Dog{
        Name: "小黑",
    }
    var d1 Animal = d
    fmt.Println(d1.GetName())
    d1.SetName("泰迪")
    fmt.Println(d1.GetName())
    //cat实现animal接口
    c := &Cat{
        Name: "小黑",
    }
    var c1 Animal = c
    fmt.Println(c1.GetName())
    c1.SetName("大菊")
    fmt.Println(c1.GetName())
}

一个结构体实现多个接口

package main

import "fmt"

type Animal interface {
    SetName(string)
}

type Animal1 interface {
    GetName() string
}

type Dog struct {
    Name string
}

func (d *Dog) SetName(name string) {
    d.Name = name
}
func (d Dog) GetName() string {
    return d.Name
}

func main() {
    //Dog实现animal和animal1接口(一次实现多个接口)
    d := &Dog{
        Name: "小黑",
    }
    var d1 Animal = d
    var d2 Animal1 = d
    d1.SetName("泰迪")
    fmt.Println(d2.GetName())
}

接口嵌套

package main

import "fmt"

type Ainterface interface {
    SetName(string)
}

type Binterface interface {
    GetName() string
}

type Animaler interface { //接口的嵌套
    Ainterface
    Binterface
}
type Dog struct {
    Name string
}

func (d *Dog) SetName(name string) {
    d.Name = name
}
func (d Dog) GetName() string {
    return d.Name
}

func main() {
    d := &Dog{
        Name: "小黑",
    }
    var d1 Animaler = d
    d1.SetName("泰迪")
    fmt.Println(d1.GetName())
}

空接口和类型断言使用细节-类型断言输出空接口类型结构体属性(四)

package main

import "fmt"

type Address struct {
    Name  string
    Phone int
}

// Golang中空接口和类型断言使用细节

func main() {
    var userinfo = make(map[string]interface{})
    userinfo["username"] = "张三"
    userinfo["age"] = 20
    userinfo["爱好"] = []string{"睡觉", "吃饭"}
    fmt.Println(userinfo["age"])
    fmt.Println(userinfo["爱好"])
    //fmt.Println(userinfo["爱好"][1])//(type interface {} does not support indexing),空接口不支持索引
    var address = Address{
        Name:  "李四",
        Phone: 15200010001,
    }
    fmt.Println(address.Name)
    userinfo["address"] = address
    fmt.Println(userinfo["address"])
    //var name = userinfo["address"].Name//(type interface {} is interface with no methods) ,空接口没有Name方法
    //fmt.Println(name)
    hobby2, _ := userinfo["爱好"].([]string)       //通过类型断言,返回切片和断言是否成功
    fmt.Println(hobby2[1])                       //通过切片索引数据
    address2, _ := userinfo["address"].(Address) //通过类型断言打印结构体的属性
    fmt.Println(address2.Name, address2.Phone) //李四 15200010001
}
登陆评论: 使用GITHUB登陆