Go 语言 在线

2175Go 错误处理

if result, errorMsg := Divide(100, 10); errorMsg == "" {
    fmt.Println("100/10 = ", result)
}

if _, errorMsg := Divide(100, 0); errorMsg != "" {
    fmt.Println("errorMsg is: ", errorMsg)
}

等价于:

result, errorMsg := Divide(100,10)
if errorMsg == ""{
    fmt.Println("100/10 = ", result)
}

result, errorMsg = Divide(100,0)
if errorMsg != ""{
    fmt.Println("errorMsg is: ", errorMsg)
}

2174Go 错误处理

这里应该介绍一下 panic 与 recover,一个用于主动抛出错误,一个用于捕获panic抛出的错误。

概念

panic 与 recover 是 Go 的两个内置函数,这两个内置函数用于处理 Go 运行时的错误,panic 用于主动抛出错误,recover 用来捕获 panic 抛出的错误。

  • 引发panic有两种情况,一是程序主动调用,二是程序产生运行时错误,由运行时检测并退出。
  • 发生panic后,程序会从调用panic的函数位置或发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,直到被recover捕获或运行到最外层函数。
  • panic不但可以在函数正常流程中抛出,在defer逻辑里也可以再次调用panic或抛出panicdefer里面的panic能够被后续执行的defer捕获。
  • recover用来捕获panic,阻止panic继续向上传递。recover()defer一起使用,但是defer只有在后面的函数体内直接被掉用才能捕获panic来终止异常,否则返回nil,异常继续向外传递。

例子1

//以下捕获失败
defer recover()
defer fmt.Prinntln(recover)
defer func(){
    func(){
        recover() //无效,嵌套两层
    }()
}()

//以下捕获有效
defer func(){
    recover()
}()

func except(){
    recover()
}
func test(){
    defer except()
    panic("runtime error")
}

例子2

多个panic只会捕捉最后一个:

package main
import "fmt"
func main(){
    defer func(){
        if err := recover() ; err != nil {
            fmt.Println(err)
        }
    }()
    defer func(){
        panic("three")
    }()
    defer func(){
        panic("two")
    }()
    panic("one")
}

使用场景

一般情况下有两种情况用到:

  • 程序遇到无法执行下去的错误时,抛出错误,主动结束运行。
  • 在调试程序时,通过 panic 来打印堆栈,方便定位错误。

2173Go 语言接口

将接口做为参数

package main

import (
    "fmt"
)

type Phone interface {
    call() string 
}

type Android struct {
    brand string
}

type IPhone struct {
    version string
}

func (android Android) call() string {
    return "I am Android " + android.brand
}

func (iPhone IPhone) call() string {
    return "I am iPhone " + iPhone.version
}

func printCall(p Phone) {
    fmt.Println(p.call() + ", I can call you!")
}

func main() {
    var vivo = Android{brand:"Vivo"}
    var hw = Android{"HuaWei"}

    i7 := IPhone{"7 Plus"}
    ix := IPhone{"X"}

    printCall(vivo)
    printCall(hw)
    printCall(i7)
    printCall(ix)
}

输出结果:

I am Android Vivo, I can call you!
I am Android HuaWei, I can call you!
I am iPhone 7 Plus, I can call you!
I am iPhone X, I can call you!

2172Go 语言接口

接口案例:

package main
import (
    "fmt"
)


//定义接口
type Phone interface {
    call()
    call2()
}


//一直都搞不懂这是干啥的
//原来是用来定义结构体内的数据类型的

type Phone1 struct {
    id            int
    name          string
    category_id   int
    category_name string
}

//第一个类的第一个回调函数
func (test Phone1) call() {
    fmt.Println("这是第一个类的第一个接口回调函数 结构体数据:", Phone1{id: 1, name: "浅笑"})
}

//第一个类的第二个回调函数
func (test Phone1) call2() {
    fmt.Println("这是一个类的第二个接口回调函数call2", Phone1{id: 1, name: "浅笑", category_id: 4, category_name: "分类名称"})
}



//第二个结构体的数据类型
type Phone2 struct {
    member_id       int
    member_balance  float32
    member_sex      bool
    member_nickname string
}

//第二个类的第一个回调函数
func (test2 Phone2) call() {
    fmt.Println("这是第二个类的第一个接口回调函数call", Phone2{member_id: 22, member_balance: 15.23, member_sex: false, member_nickname: "浅笑18"})
}


//第二个类的第二个回调函数
func (test2 Phone2) call2() {
    fmt.Println("这是第二个类的第二个接口回调函数call2", Phone2{member_id: 44, member_balance: 100, member_sex: true, member_nickname: "陈超"})
}

//开始运行
func main() {
    var phone Phone
    
    //先实例化第一个接口
    phone = new(Phone1)
    phone.call()
    phone.call2()

    //实例化第二个接口
    phone = new(Phone2)
    phone.call()
    phone.call2()
}

2171Go 语言接口

接口方法传参,以及返回结果:

package main

import "fmt"

type Phone interface {
    call(param int) string
    takephoto()
}

type Huawei struct {
}

func (huawei Huawei) call(param int) string{
    fmt.Println("i am Huawei, i can call you!", param)
    return "damon"
}

func (huawei Huawei) takephoto() {
    fmt.Println("i can take a photo for you")
}

func main(){
    var phone Phone
    phone = new(Huawei)
    phone.takephoto()
    r := phone.call(50)
    fmt.Println(r)
}