8小时速成golang(四)语法新奇 defer slice 和 map 面向对象的特征 封装 继承 多态

5、defer

defer语句被用于预定对一个函数的调用。可以把这类被defer语句调用的函数称为延迟函数。
defer的实际应用场景, 例如defer语句会将其后的函数调用推迟到当前函数执行结束时执行。这个特性常用于处理成对的操作,如打开/关闭文件、获取/释放锁、连接/断开连接等,确保资源被适当地释放,即使在发生错误或提前返回的情况下也能保证执行。

defer作用:
释放占用的资源
捕捉处理异常
输出日志
func Demo(){
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
    defer fmt.Println("4")
}
func main() {
    Demo()
}

 

结果
4
3
2
1

 

一、defer执行顺序

如果一个函数中有多个defer语句,它们会以LIFO(后进先出)的顺序执行。
 
package main

//知识点一:defer 的执行顺序package main
import "fmt"
func func1(){
    fmt.Println("A")
}
func func2(){
    fmt.Println("B")
}
func func3(){
    fmt.Println("C")
}
func main(){
    defer func1()
    defer func2()
    defer func3()
}

 defer是当前函数的声明周期全部结束再结束的 即}之后 所以defer是在return之后

//知识点二: defer和return谁先谁后
package main
import "fmt"
func deferFunc()int{
	fmt.Println("defer func called...")
	return 0
}
func returnFunc()int{
	fmt.Println("return func called...")
	return 0
}
func returnAndDefer()int {
	defer deferFunc()
	return returnFunc()
}
func main() {
	returnAndDefer()
}

 结果

return func called...
defer func called...

 

recover错误拦截
func recover interface{}

 

运行时panic异常一旦被引发就会导致程序崩溃。
Go语言提供了专用于“拦截”运行时panic的内建函数“recover”。它可以是当前的程序从运行时panic的状态中恢复并重新获得流程控制权。
注意:recover只有在defer调用的函数中有效。

package main

import "fmt"

func Demo(i int) {
    //定义10个元素的数组
    var arr [10]int
    //错误拦截要在产生错误前设置
    defer func() {
        //设置recover拦截错误信息
        err := recover()
        //产生panic异常  打印错误信息
        if err != nil {
            fmt.Println(err)
        }
    }()
    //根据函数参数为数组元素赋值
    //如果i的值超过数组下标 会报错误:数组下标越界
    arr[i] = 10

}

func main() {
    Demo(10)
    //产生错误后 程序继续
    fmt.Println("程序继续执行...")
}

 结果

runtime error: index out of range
程序继续执行...

 

 

6、slice和map

Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),
与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
定义切片
你可以声明一个未指定大小的数组来定义切片:
var identifier []type

切片不需要说明长度。

或使用make()函数来创建切片:

var slice1 []type = make([]type, len)

也可以简写为

slice1 := make([]type, len)

也可以指定容量,其中capacity为可选参数。

make([]T, length, capacity)

这里 len 是数组的长度并且也是切片的初始长度。

切片初始化

s :=[] int {1,2,3 }

直接初始化切片,[]表示是切片类型,{1,2,3}初始化值依次是1,2,3.其cap=len=3

s := arr[:]

初始化切片s,是数组arr的引用

 

s := arr[startIndex:endIndex]

将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片

 

s := arr[startIndex:]

缺省endIndex时将表示一直到arr的最后一个元素

 

s := arr[:endIndex]

通过切片s初始化切片s1

 

s :=make([]int,len,cap)

通过内置函数make()初始化切片s,[]int 标识为其元素类型为int的切片

package main
import "fmt"

func main(){
    //固定长度的数组
    var myArray1 [10]int
    myArray2 :=[10]int{1,2,3,4}
    myArray3 :=[4]int{11,22,33,44}
    //法1用循环
    fmt.Println("myArray1")
    for i :=0;i<len(myArray1); i++ {
        fmt.Print(myArray1[i])
    }
    //法2用for each
    fmt.Println("myArray2")
    for index,value := range myArray2 {
        fmt.Println("index=",index,",value =",value)
    }
    //查看数组的数据类型
    fmt.Printf("myArray1 types =%T\n", myArray1)
    fmt.Printf("myArray2 types =%T\n", myArray2)
    fmt.Printf("myArray3 types =%T\n", myArray3)
}

 

 

固定长度的数组作为形参是值拷贝的形式

动态数组即切片的形式形参是引用拷贝的形式,可以任意改变值

package main
import "fmt"
func printArray(myArray []int) {
    //引用传递
    //- 表示匿名的变量
    for _, value := range myArray {
        fmt.Println("value =", value)
    }
    myArray[0] = 100
}
func main(){
        myArray :=[]int{1,2,3,4}//动态数组,切片 slice
        fmt.Printf("myArray type is %T\n", myArray)
        printArray(myArray)
        fmt.Println(" ====")
        for _,value := range myArray{
            fmt.Println("value =",value)
        }
}

 

 

len() 和 cap() 函数

切片是可索引的,并且可以由 len() 方法获取长度。

切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

以下为具体实例:

package main
import "fmt"

func main() {
   var numbers = make([]int,3,5)

   printSlice(numbers)
}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

 结果

len=3 cap=5 slice=[0 0 0]

空(nil)切片

一个切片在未初始化之前默认为 nil,长度为 0,实例如下

package main

import "fmt"
func main() {
   var numbers []int
   printSlice(numbers)

   if(numbers == nil){
      fmt.Printf("切片是空的")
   }
}


func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

结果

len=0 cap=0 slice=[]
切片是空的

切片截取

可以通过设置下限及上限来设置截取切片[lower-bound:upper-bound],实例如下:

package main
import "fmt"
func main() {
   /* 创建切片 */
   numbers := []int{0,1,2,3,4,5,6,7,8}   
   printSlice(numbers)


   /* 打印原始切片 */
   fmt.Println("numbers ==", numbers)


   /* 打印子切片从索引1(包含) 到索引4(不包含)*/
   fmt.Println("numbers[1:4] ==", numbers[1:4])


   /* 默认下限为 0*/
   fmt.Println("numbers[:3] ==", numbers[:3])


   /* 默认上限为 len(s)*/
   fmt.Println("numbers[4:] ==", numbers[4:])


   numbers1 := make([]int,0,5)
   printSlice(numbers1)


   /* 打印子切片从索引  0(包含) 到索引 2(不包含) */
   number2 := numbers[:2]
   printSlice(number2)


   /* 打印子切片从索引 2(包含) 到索引 5(不包含) */
   number3 := numbers[2:5]
   printSlice(number3)


}


func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

结果

len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
numbers == [0 1 2 3 4 5 6 7 8]
numbers[1:4] == [1 2 3]
numbers[:3] == [0 1 2]
numbers[4:] == [4 5 6 7 8]
len=0 cap=5 slice=[]
len=2 cap=9 slice=[0 1]
len=3 cap=7 slice=[2 3 4]

append() 和 copy() 函数

如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。

下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。

package main
import "fmt"
func main() {
   var numbers []int
   printSlice(numbers)


   /* 允许追加空切片 */
   numbers = append(numbers, 0)
   printSlice(numbers)


   /* 向切片添加一个元素 */
   numbers = append(numbers, 1)
   printSlice(numbers)


   /* 同时添加多个元素 */
   numbers = append(numbers, 2,3,4)
   printSlice(numbers)


   /* 创建切片 numbers1 是之前切片的两倍容量*/
   numbers1 := make([]int, len(numbers), (cap(numbers))*2)


   /* 拷贝 numbers 的内容到 numbers1 */
   copy(numbers1,numbers)
   printSlice(numbers1)   
}


func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

结果

len=0 cap=0 slice=[]
len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=5 cap=6 slice=[0 1 2 3 4]
len=5 cap=12 slice=[0 1 2 3 4]

map

map和slice类似,只不过是数据结构不同,下面是map的一些声明方式。

第三种声明方式属于直接初始化 所以不需要make空间出来

一、map的声明

package main
import (
    "fmt"
)

func main() {
    //第一种声明
    var test1 map[string]string
    //在使用map前,需要先make,make的作用就是给map分配数据空间
    test1 = make(map[string]string, 10) 
    test1["one"] = "php"
    test1["two"] = "golang"
    test1["three"] = "java"
    fmt.Println(test1) //map[two:golang three:java one:php]


    //第二种声明
    test2 := make(map[string]string)
    test2["one"] = "php"
    test2["two"] = "golang"
    test2["three"] = "java"
    fmt.Println(test2) //map[one:php two:golang three:java]

    //第三种声明
    test3 := map[string]string{
        "one" : "php",
        "two" : "golang",
        "three" : "java",
    }
    fmt.Println(test3) //map[one:php two:golang three:java]


    
    language := make(map[string]map[string]string)
    language["php"] = make(map[string]string, 2)
    language["php"]["id"] = "1"
    language["php"]["desc"] = "php是世界上最美的语言"
    language["golang"] = make(map[string]string, 2)
    language["golang"]["id"] = "2"
    language["golang"]["desc"] = "golang抗并发非常good"
    
    fmt.Println(language) //map[php:map[id:1 desc:php是世界上最美的语言] golang:map[id:2 desc:golang抗并发非常good]]


    //增删改查
    // val, key := language["php"]  //查找是否有php这个子元素
    // if key {
    //     fmt.Printf("%v", val)
    // } else {
    //     fmt.Printf("no");
    // }

    //language["php"]["id"] = "3" //修改了php子元素的id值
    //language["php"]["nickname"] = "啪啪啪" //增加php元素里的nickname值
    //delete(language, "php")  //删除了php子元素
    fmt.Println(language)
}

二、map的操作 增删改查

package main
import "fmt"
func main() {
    cityMap := make(map[string]string)

    //添加
    cityMap["china"]="Beijing"
    cityMap["Japan"]="Tokyo"
    cityMap["USA"]="NewYork"

    //删除
    delete(cityMap,"Japan")

    //修改
    cityMap["USA"]="DC"
    fmt.Println("------")
    //遍历
    for key,value :=range cityMap{
        fmt.Println("key =",key)
        fmt.Println("value =",value)
    }
}

结果

------
key = china
value = Beijing
key = USA
value = DC

 

 

7、面向对象特征

一、面向对象类的  表示 与 封装

==================================================================

类名、属性名、方法名 首字母大写表示对外(其他包)可以访问,否则只能够在本包内访问

==================================================================

package main
import "fmt"
//如果类名首字母大写,表示其地台也能够访问
type Hero struct{
//如果说类的属性首字母大写,表承设属性是对外能够访问的,香则的话只能够当前的内能访问
    Name string
    Ad int
    level int
}

func(this *Hero) Show(){
    fmt.Println("Name:",this.Name)
    fmt.Println("Ad:",this.Ad)
    fmt.Println("Level:",this.level)
}

func(this *Hero) GetName() string{
    return this.Name
}

func (this *Hero) SetName(newName string){
//this 是调用该方法的对象的一个副本(拷贝)
    this.Name = newName
}

func main() {
    hero :=Hero{Name:"zhang3",Ad:100}
    hero.Show()
    hero.SetName("li4")
    hero.Show()
    fmt.Println()
}

结果

Name: zhang3
Ad: 100
Level: 0
Name: li4
Ad: 100
Level: 0

 

二、面向对象的继承

 没有私有化 公有化 只有对包内和包外

package main
import "fmt"
type Human struct {
    name string
    sex  string
}

func (this *Human) Eat() {
    fmt.Println("Human.Eat()...")
}

func (this *Human) Walk() {
    fmt.Println("Human.Walk()...")
}

//=================

type SuperMan struct {
    Human //SuperMan类继承了Human类的方法
    level int
}

//重定义父类的方法Eat()
func (this *SuperMan) Eat() {
    fmt.Println("SuperMan.Eat()...")
}

//子类的新方法
func (this *SuperMan) Fly() {
    fmt.Println("SuperMan.Fly()...")
}

func (this *SuperMan) Print() {
    fmt.Println("name = ", this.name)
    fmt.Println("sex = ", this.sex)
    fmt.Println("level = ", this.level)
}

func main() {
    h := Human{"zhang3", "female"}

    h.Eat()
    h.Walk()

    //定义一个子类对象
    //s := SuperMan{Human{"li4", "female"}, 88}
    var s SuperMan
    s.name = "li4"
    s.sex = "male"
    s.level = 88
    s.Walk() //父类的方法
    s.Eat()  //子类的方法
    s.Fly()  //子类的方法
    s.Print()
}

结果

Human.Eat()...
Human.Walk()...
Human.Walk()...
SuperMan.Eat()...
SuperMan.Fly()...
name =  li4
sex =  male
level =  88

 

 

方法
假设有两个方法,一个方法的接收者是指针类型,一个方法的接收者是值类型,那么:

●对于值类型的变量和指针类型的变量,这两个方法有什么区别?
●如果这两个方法是为了实现一个接口,那么这两个方法都可以调用吗?
●如果方法是嵌入到其他结构体中的,那么上面两种情况又是怎样的?
 
 
package main
import "fmt"

//定义一个结构体
type Book struct {
    title string
    auth string
}

func changeBook(book Book) {
    //值传递,传递一个book的副本
    book.auth = "Jams"
}
func changeBook2(book *Book) {
    //指针传递
    book.auth = "777"
}

func main() {
    var book1 Book
    book1.title = "Golang"
    book1.auth = "zhang3"
    changeBook(book1)
    fmt.Printf("%v\n",book1)

    changeBook2(&book1)
    fmt.Printf("%v\n",book1)
}

运行结果

{Golang zhang3}
{Golang 777}

 

 
package main
import "fmt"

//定义一个结构体
type T struct {
    name string
}
func (t T) method1() { t.name = "new name1" }
func (t *T) method2() { t.name = "new name2" }
func main() { t := T{"old name"} fmt.Println("method1 调用前 ", t.name) t.method1() fmt.Println("method1 调用后 ", t.name) fmt.Println("method2 调用前 ", t.name) t.method2() fmt.Println("method2 调用后 ", t.name) }

结果

method1 调用前  old name
method1 调用后  old name
method2 调用前  old name
method2 调用后  new name2

当调用t.method1()时相当于method1(t),实参和行参都是类型 T,可以接受。此时在method1()中的t只是参数t的值拷贝,所以method1()的修改影响不到main中的t变量。

当调用t.method2()=>method2(t),这是将 T 类型传给了 *T 类型,go可能会取 t 的地址传进去:method2(&t)。所以 method1() 的修改可以影响 t。

T 类型的变量这两个方法都是拥有的。

 

方法值和方法表达式

方法值

我们经常选择一个方法,并且在同一个表达式里执行,比如常见的p.Distance()形式,

实际上将其分成两步来执行也是可能的。p.Distance叫作“选择器”,选择器会返回一个方法"值"一个将方法(Point.Distance)

绑定到特定接收器变量的函数。这个函数可以不通过指定其接收器即可被调用;即调用时不需要指定接收器,只要传入函数的参数即可:

package main
import "fmt"
import "math"
type Point struct{ X, Y float64 }
//这是给struct Point类型定义一个方法
func (p Point) Distance(q Point) float64 {
    return math.Hypot(q.X-p.X, q.Y-p.Y)
}

func main() {
    p := Point{1, 2}
    q := Point{4, 6}

    distanceFormP := p.Distance   // 方法值(相当于C语言的函数地址,函数指针)
    fmt.Println(distanceFormP(q)) // "5"
    fmt.Println(p.Distance(q))    // "5"


    //实际上distanceFormP 就绑定了 p接收器的方法Distance


    distanceFormQ := q.Distance   //
    fmt.Println(distanceFormQ(p)) // "5"
    fmt.Println(q.Distance(p))    // "5"


    //实际上distanceFormQ 就绑定了 q接收器的方法Distance
}

在一个包的API需要一个函数值、且调用方希望操作的是某一个绑定了对象的方法的话,方法"值"会非常实用.

举例来说,下面例子中的time.AfterFunc这个函数的功能是在指定的延迟时间之后来执行一个(译注:另外的)函数。且这个函数操作的是一个Rocket对象r

type Rocket struct { /* ... */ }
func (r *Rocket) Launch() { /* ... */ }
r := new(Rocket)
time.AfterFunc(10 * time.Second, func() { r.Launch() })

直接用方法"值"传入AfterFunc的话可以更为简短:

time.AfterFunc(10 * time.Second, r.Launch)

省掉了上面那个例子里的匿名函数。

方法表达式

和方法"值"相关的还有方法表达式。当调用一个方法时,与调用一个普通的函数相比,我们必须要用选择器(p.Distance)语法来指定方法的接收器。

当T是一个类型时,方法表达式可能会写作T.f或者(*T).f,会返回一个函数"值",这种函数会将其第一个参数用作接收器,

所以可以用通常(译注:不写选择器)的方式来对其进行调用:

package main
import "fmt"
import "math"


type Point struct{ X, Y float64 }

//这是给struct Point类型定义一个方法
func (p Point) Distance(q Point) float64 {
    return math.Hypot(q.X-p.X, q.Y-p.Y)
}

func main() {
    p := Point{1, 2}
    q := Point{4, 6}
    distance1 := Point.Distance //方法表达式, 是一个函数值(相当于C语言的函数指针)
    fmt.Println(distance1(p, q))
    fmt.Printf("%T\n", distance1) //%T表示打出数据类型 ,这个必须放在Printf使用


    distance2 := (*Point).Distance //方法表达式,必须传递指针类型
    distance2(&p, q)
    fmt.Printf("%T\n", distance2)
}

执行结果

5
func(main.Point, main.Point) float64
func(*main.Point, main.Point) float64
// 这个Distance实际上是指定了Point对象为接收器的一个方法func (p Point) Distance(),
// 但通过Point.Distance得到的函数需要比实际的Distance方法多一个参数,
// 即其需要用第一个额外参数指定接收器,后面排列Distance方法的参数。
// 看起来本书中函数和方法的区别是指有没有接收器,而不像其他语言那样是指有没有返回值。

当你根据一个变量来决定调用同一个类型的哪个函数时,方法表达式就显得很有用了。

你可以根据选择来调用接收器各不相同的方法。下面的例子,变量op代表Point类型的addition或者subtraction方法,

Path.TranslateBy方法会为其Path数组中的每一个Point来调用对应的方法:

package main


import "fmt"
import "math"


type Point struct{ X, Y float64 }


//这是给struct Point类型定义一个方法
func (p Point) Distance(q Point) float64 {
        return math.Hypot(q.X-p.X, q.Y-p.Y)
}


func (p Point) Add(another Point) Point {
        return Point{p.X + another.X, p.Y + another.Y}
}


func (p Point) Sub(another Point) Point {
        return Point{p.X - another.X, p.Y - another.Y}
}


func (p Point) Print() {
        fmt.Printf("{%f, %f}\n", p.X, p.Y)
}


//定义一个Point切片类型 Path
type Path []Point


//方法的接收器 是Path类型数据, 方法的选择器是TranslateBy(Point, bool)
func (path Path) TranslateBy(another Point, add bool) {
        var op func(p, q Point) Point //定义一个 op变量 类型是方法表达式 能够接收Add,和 Sub方法
        if add == true {
                op = Point.Add //给op变量赋值为Add方法
        } else {
                op = Point.Sub //给op变量赋值为Sub方法
        }


        for i := range path {
                //调用 path[i].Add(another) 或者 path[i].Sub(another)
                path[i] = op(path[i], another)
                path[i].Print()
        }
}


func main() {

        points := Path{
                {10, 10},
                {11, 11},
        }

        anotherPoint := Point{5, 5}

        points.TranslateBy(anotherPoint, false)

        fmt.Println("------------------")

        points.TranslateBy(anotherPoint, true)
}

运行结果

{5.000000, 5.000000}
{6.000000, 6.000000}
------------------
{10.000000, 10.000000}
{11.000000, 11.000000}

 

 

三、面向对象的多态 实现与基本

 

①基本元素

1、有一个父类

2、由子类实现父类的方法

3、父类类型的变量(指针)  指向   引用子类的具体变量

通俗的讲  我调用你父类 父类却是让子类具体实现方法去做事

package main
import "fmt"
//本质是一个指针
type AnimalIF interface {
    Sleep()
    GetColor() string //获取动物的颜色
    GetType() string  //获取动物的种类
}

//具体的类
type Cat struct {
    //继承具体的类的话 需要在这边写入类名
    //继承接口不需要写入 只需要实现继承的类的方法
    color string //猫的颜色
}

func (this *Cat) Sleep() {
    fmt.Println("Cat is Sleep")
}

func (this *Cat) GetColor() string {
    return this.color
}

func (this *Cat) GetType() string {
    return "Cat"
}

//具体的类
type Dog struct {
    color string
}

func (this *Dog) Sleep() {
    fmt.Println("Dog is Sleep")
}

func (this *Dog) GetColor() string {
    return this.color
}

func (this *Dog) GetType() string {
    return "Dog"
}

func showAnimal(animal AnimalIF) {
    animal.Sleep() //多态
    fmt.Println("color = ", animal.GetColor())
    fmt.Println("kind = ", animal.GetType())
}

func main() {

    var animal AnimalIF //接口的数据类型, 父类指针
    animal = &Cat{"Green"}

    animal.Sleep() //调用的就是Cat的Sleep()方法 , 多态的现象

    animal = &Dog{"Yellow"}

    animal.Sleep() // 调用Dog的Sleep方法,多态的现象

    cat := Cat{"Green"}
    dog := Dog{"Yellow"}

    showAnimal(&cat)
    showAnimal(&dog)
}

结果

 

 

 

② 通用万能类型,同一个服务用到的相同包一定要调同一个地方的,否则会报错

1、通用万能类型,空接口,interface{ }可以引用任何的基本数据类型,int,float32,string,struct,float64 

2、通用万能类型 类型断言,选择 参数是   interface{ }

 

Golang的语言中提供了断言的功能。

golang中的所有程序都实现了interface{}的接口,这意味着,所有的类型如string,int,int64甚至是自定义的struct类型都就此拥有了interface{}的接口,

这种做法和java中的Object类型比较类似。

那么在一个数据通过func funcName(interface{})的方式传进来的时候,也就意味着这个参数被自动的转为interface{}的类型。

func funcName(a interface{}) string {
     return string(a)
}

结果

cannot convert a (type interface{}) to type string: need type assertion

 

此时,意味着整个转化的过程需要类型断言。类型断言有以下几种形式:

 

1)直接断言使用

var a interface{}

fmt.Println("Where are you,Jonny?", a.(string))

但是如果断言失败一般会导致panic的发生。所以为了防止panic的发生,我们需要在断言前进行一定的判断

如果断言失败,那么ok的值将会是false,但是如果断言成功ok的值将会是true,同时value将会得到所期待的正确的值。示例:

value, ok := a.(string)
if !ok {
    fmt.Println("It's not ok for type string")
    return
}
fmt.Println("The value is ", value) 

完整例子

package main


import "fmt"


/*
func funcName(a interface{}) string {
        return string(a)
}
*/


func funcName(a interface{}) string {
        value, ok := a.(string)
        if !ok {
                fmt.Println("It is not ok for type string")
                return ""
        }
        fmt.Println("The value is ", value)


        return value
}


func main() {
        //      str := "123"
        //      funcName(str)
        //var a interface{}
        //var a string = "123"
        var a int = 10
        funcName(a)
}

 

2)配合switch使用

var t interface{}
t = functionOfSomeType()

switch t := t.(type) { default: fmt.Printf("unexpected type %T", t) // %T prints whatever type t has case bool: fmt.Printf("boolean %t\n", t) // t has type bool case int: fmt.Printf("integer %d\n", t) // t has type int case *bool: fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool case *int: fmt.Printf("pointer to integer %d\n", *t) // t has type *int }

 

或者如下使用方法

func sqlQuote(x interface{}) string {
    if x == nil {
        return "NULL"
    } else if _, ok := x.(int); ok {
        return fmt.Sprintf("%d", x)
    } else if _, ok := x.(uint); ok {
        return fmt.Sprintf("%d", x)
    } else if b, ok := x.(bool); ok {
        if b {
            return "TRUE"
        }
        return "FALSE"
    } else if s, ok := x.(string); ok {
        return sqlQuoteString(s) // (not shown)
    } else {
        panic(fmt.Sprintf("unexpected type %T: %v", x, x))
    }
}

 

 

 

 

 

例子1:做是否为0值判断

    in := 0.
    var tmp interface{} = float32(in)
    fmt.Println("float 0==0:", in == 0)
fmt.Println(
"float -> interface{} -> float", tmp.(float32) == 0)
switch v := tmp.(type) { case float32: fmt.Println("float -> interface -.type-> float", v == 0) }

结果

float 0==0: true
float -> interface{} -> float true
float -> interface -.type-> float true

 

 

例子2:判断数据类型是否为string

package main
import "fmt"

//interface{}是万能数据类型
func myFunc(arg interface{}) {
    fmt.Println("myFunc is called...")
    fmt.Println(arg)

    //interface{} 如何区分 此时引用的底层数据类型到底是什么?

    //给 interface{} 提供 “类型断言” 的机制
    value, ok := arg.(string)
    if !ok {
        fmt.Println("arg is not string type")
    } else {
        fmt.Println("arg is string type, value = ", value)

        fmt.Printf("value type is %T\n", value)
    }
}

type Booking struct {
    auth string
}

func main() {
    book := Booking{"Golang"}
    myFunc(book)
    myFunc(100)
    myFunc("abc")
    myFunc(3.14)
}

 

 

 

 

 

 

 

 

 

posted @ 2024-02-12 23:21  陈晓猛  阅读(33)  评论(0编辑  收藏  举报