为什么在Go语言中要少用interface{}

时间:2025-02-24 07:51:44

记得刚从Java转Go的时候,一个用Go语言的前辈告诉我:“要少用interface{},这玩意儿很好用,但是最好不要用。”那时候我的组长打趣接话:“不会,他是从Java转过来的,碰到个问题就想定义个类。”当时我对interface{}的第一印象也是类比Java中的Object类,我们使用Java肯定不会到处去传Object啊。后来的事实证明,年轻人毕竟是年轻人,看着目前项目里漫天飞的interface{},它们时而变成函数形参让人摸不着头脑;时而隐藏在结构体字段中变化无穷。不禁想起以前看到的一句话:“动态语言一时爽,重构代码火葬场。”故而写下此篇关于interface{}的经验总结,供以后的自己和读者参考。

      • 1. interface{}之对象转型坑
      • 2. interface{}之nil坑
      • 3. 总结和引用

1. interface{}之对象转型坑

一个语言用的久了,难免使用者的思维会受到这个语言的影响,interface{}作为Go的重要特性之一,它代表的是一个类似*void的指针,可以指向不同类型的数据。所以我们可以使用它来指向任何数据,这会带来类似与动态语言的便利性,如以下的例子:

type BaseQuestion struct{
    QuestionId int
    QuestionContent string
}

type ChoiceQuestion struct{
    BaseQuestion
    Options []string
}

type BlankQuestion struct{
    BaseQuestion
    Blank string
}

func fetchQuestion(id int) (interface{} , bool) {
    data1 ,ok1 := fetchFromChoiceTable(id) // 根据ID到选择题表中找题目,返回(ChoiceQuestion)
    data2 ,ok2 := fetchFromBlankTable(id)  // 根据ID到填空题表中找题目,返回(BlankQuestion)

    if ok1 {
        return data1,ok1
    }

    if ok2 {
        return data2,ok2
    }

    return nil ,false
}

在上面的代码中,data1是ChoiceQuestion类型,data2是BlankQuestion类型。因此,我们的interface{}指代了三种类型,分别是ChoiceQuestionBlankQuestionnil,这里就体现了Go和面向对象语言的不同点了,在面向对象语言中,我们本可以这么写:

func fetchQuestion(id int) (BaseQuestion , bool) {
    ...
}

只需要返回基类BaseQuestion即可,需要使用子类的方法或者字段只需要向下转型。然而在Go中,并没有这种is-A的概念,代码会无情的提示你,返回值类型不匹配。
那么,我们该如何使用这个interface{}返回值呢,我们也不知道它是什么类型啊。所以,你得不厌其烦的一个一个判断:

func printQuestion(){
    if data, ok := fetchQuestion(1001); ok {
        switch v := data.(type) {
        case ChoiceQuestion:
            (v)
        case BlankQuestion:
            (v)
        case nil:
            (v)
        }
        (data)
    }
}

// ------- 输出--------