专栏首页网管叨bi叨Go 语言里怎么正确实现枚举?答案藏着官方的源码里

Go 语言里怎么正确实现枚举?答案藏着官方的源码里

在编程领域里,枚举是用来表示只包含有限数量的固定值的类型,在开发中一般用于标识错误码或者状态机。拿一个实体对象的状态机来说,它通常与这个对象在数据库里对应记录的标识状态的字段值相对应。

在刚开始学编程的时候,你一定写过,至少见过直接使用魔术数字进行判断的代码。啥叫魔术数字呢,举个例子,要置顶一个文章的时候先判断文章是不是已发布状态。

if (article.state == 2) {
   // state 2 代表文章已发布
}

假如我们的代码里没有注释,或者等我们项目的代码里充斥着这些魔术数字的判断的时候,你是不是会很头疼?

后来我就学会了把这些状态值定义成常量,并且也搞一个判断对象状态的方法单独封装这段逻辑。

public class ArticleState {
    
    public static final int Draft = 1; //草稿
    
    public static final int Published = 2; //发布
    
    public static final int Deleted = 3; // 已删除
}

public  Boolean checkArticleState(int state) {
    
    ...
    
}

这种用法,肯定是比在程序里直接用魔术数字进行判断要强很多啦,至少看着不会很头疼,不会想骂**。

不过后来被当时带我的老大哥说这种也有缺点,上面这个 checkArticleState 方法用来检查文章状态,本意是让调用者传入 ArticleState 的三个静态常量之一,但由于没有类型上的约束,因此传入任意一个 int 值在语法上也是允许的,编译器也不会提出任何警告,换成用枚举更合适一些。

em~! 我不记得大学教 Java 的那个学期老师讲过这玩意啊,莫非又是一个上课玩手机错过的知识点?......

所以使用枚举后我们的Java代码变成了:

// 使用enum而非class声明
public enum ArticleState {
 
    //要在enum里创建所有的枚举对象
    Draft(1, "草稿");
    Published(2, "已发布");
    Deleted(3, "已删除")
      
    // 自定义属性
    private int code;

    private String text;
  
    // 构造方法必须是private的
    ArticleState(int code, String text) {
        this.code = id;
        this.text = name;
    }
}

public  Boolean checkArticleState(ArticleState state) {
    
    ...
    
}

这样就能靠形参的枚举类型帮我们过滤掉非法的状态值。把整型值作为参数传给 checkArticleState 方法时因为类型不匹配编译不过去,在写代码时编译器也能马上提示出来。

如果没有用过 Java 的小伙伴也不用纠结,主要的语法点我用注释标注出来了。

后来这两年主要在用Go做项目,我发现相似的问题 Go 里也存在,但是 Go 并没有提供枚举类型,那怎么做到进行状态值的正确限制呢?如果还是用 int 型的常量肯定不行。比如:

 const (
     Draft int = 1
     Published = 2
     Deleted   = 3
 )

 const (
     Summer int = 1
     Autumn     = 2
     Winter     = 3
     Spring     = 4
 )

 func main() {
     // 输出 true, 不会有任何编译错误
     fmt.Println(Autumn == Draft)
 }

比如上面定义了两组 int 类型的常量,一类代表文章状态,一类代表季节的四季。这种方式拿文章状态与季节进行比较不会有任何编译上的错误。

答案在 Go 内置库或者一些咱们都知道的开源库的代码里就能找到。比如看看 google.golang.org/grpc/codes 里的gRPC 的错误码是怎么定义的,我们马上就能明白该怎么正确的实现枚举。

下面不多卖关子直接上答案了,不想去源码里看的,就看我这里写的也行,都是这么做的。

我们可以用 int 作为基础类型创建一个别名类型,Go 里边是支持这个的

type Season int

const (
 Summer Season = 1
 Autumn        = 2
 Winter        = 3
 Spring        = 4
)

当然定义连续的常量值的时候 Go 里边经常使用 iota,所以上面的定义还能进一步简化。

type Season int

const (
 Summer Season = iota + 1
 Autumn
 Winter
 Spring
)

type ArticleState int

const (
  Draft ArticleState = iota + 1
  Published
  Deleted  
)

func checkArticleState(ArticleState state) {
 // ... 
}

 func main() {
   // 两个操作数类型不匹配,编译错误
   fmt.Println(Autumn == Draft)
   // 参数类型不匹配,编译错误
   checkArticleState(100)
 }

虽然这些状态值的底层的类型都是 int 值,但是现在不论是进行两个不相干类型的枚举值比较,还是用整型值作为参数调用 checkArticleState 方法检查文章状态,都会造成编译错误,因为现在我们使用状态值的地方都有了类型限制。

这就是为什么针对错误码、状态机这种涉及有限数量状态值的场景下不能用整型常量而是要用枚举的原因。虽然 Go 语言里没有像 Java 一样单独提供一个 enum 表示枚举的类型,但是我们仍然能通过创建类型别名来实现枚举。

你学会了吗?(#^.^#)

- END -

文章分享自微信公众号:
网管叨bi叨

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!

作者:网管
原始发表时间:2022-01-17
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • GitHub技巧之五大隐藏技巧

    搜索无疑是大家使用最多的功能,绝大多数同学是这样搜索的:点下搜索框,输入想搜的内容,然后往往会看到一大堆搜索结果,接下来还要再去选择编程语言来过滤。 结果还是一...

    骤雨重山
  • Context是怎么在Go语言中发挥关键作用的

    Context 是 Go 语言独有的设计,在其他编程语言中很少见到类似的概念,用一句话解释 Context 在 Go 语言中的作用就是:

    KevinYan
  • GitHub这5个骚操作,99%的人不知道!

    大家好,我是鱼皮,GitHub 是程序员必备的代码开源平台。我们可以在 GitHub 上搜索和阅读项目代码,进行学习;或者复制粘贴,从而快速完成自己的项目。

    程序员鱼皮
  • 怎么让Go Modules使用私有依赖模块

    Go语言官方的依赖包管理工具Go Modules已经发布很久,从1.14版本开始更是默认自动开启了Go Modules的支持,相信很多人公司里的项目都从go v...

    KevinYan
  • 你再也不用设置 GOROOT 了

    为什么不再需要设置 GOROOT 呢?推荐读两篇英文文章,我意译了下,将它们放在了一篇里。

    波罗学
  • why哥被一道基础面试题给干懵了,一气之下写出万字长文。

    前段时间我发现书的最后还有两道 Java 基础的面试题。其中有一道,非常的基础,可以说是入门级的题,但是都把我干懵了。

    why技术
  • 五分钟用Docker快速搭建Go开发环境

    挺早以前在我写过一篇用 Docker搭建LNMP开发环境的文章:用Docker搭建Laravel开发环境,里面详细介绍了将 nginx、 mysql和 php三...

    KevinYan
  • 我在暴躁同事小张的胁迫下学会了Go的交叉编译和条件编译

    今天继续关于Go开发经验的分享,这次的主题是关于Go的交叉编译和条件编译,伴随着我对自己打不过、惹不起的壕同事小张还有运维们的碎碎念。

    KevinYan
  • 由浅到深,入门Go语言Map实现原理

    把自己学习知识进行一个总结。同时把一些可能困难、复杂难以理解的东西自我消化吸收后,简单化输出,降低他人的学习成本,提高他人的学习效率,主要为如下两点:

    用户1093396
  • Nocalhost —— 让云原生开发回归原始而又简单

    大家好,欢迎参加 CIF 大会,今天我跟大家分享的内容是:破解 Kubernetes 应用开发困局。首先做个简单的自我介绍,我是来自腾讯云 CODING Dev...

    CODING
  • 由浅到深,入门Go语言Map实现原理

    本篇文章主要以Map的读来展开分析,因为读弄明白了,其他的写、更新、删除等基本操作基本都可以猜出来了,不是么。

    twelvecoder
  • LeetCode 479. 最大回文数乘积

    中文题面:给定一个整数 n ,返回可表示为两个 n 位整数乘积的 最大回文整数 。因为答案可能非常大,所以返回它对 1337 取余 。

    JIeJaitt
  • 18张图解密新时代内存分配器TCMalloc

    我们的主要目的是掌握Go语言的内存分配原理。但是呢,Go语言的内存分配主要是基于TCMalloc内存分配器实现的。所以,我们想搞懂Go语言的内存分配原理前,必须...

    用户1093396
  • 腾讯云数据库TDSQL精英挑战赛Q&A(实时更新)

    大家好!针对选手提及的问题技术团进行了倾情解答,内容整理如下。当前的解疑答惑已非常全面,如有疑问请仔细查阅以下Q&A,且文档内容也会实时更新给到大家。

    腾讯云数据库TDSQL
  • 修复android下webView控件的总结

    游戏中有一个收集玩家问题反馈的网页,很早之前就有同事反映说android在游戏无法上传附件,在浏览器中是可以正常使用的。最近能腾出手来的时候,就仔细看了一下这个...

    meteoric
  • 阅读源码的意义与方法

    思索了这两个问题良久,也去知乎找了一些相关话题的问答,但并没有标准答案。所以,我这里也只是记录一些我对此的看法,也许会随着 RTFSC 阅历的丰富而发生变化,我...

    mzlogin
  • Nocalhost —— 让云原生开发回归原始而又简单

    大家好,欢迎参加 CIF 大会,今天我跟大家分享的内容是:破解 Kubernetes 应用开发困局。首先做个简单的自我介绍,我是来自腾讯云 CODING Dev...

    CODING
  • Go指针的使用限制和突破之路

    大家好呀,今天网管想在这篇文章里好好跟大家聊一下 Go 语言指针这个话题,相较于 C 而言,Go 语言在设计时为了使用安全给指针在类型和运算上增加了限制,这让G...

    KevinYan
  • go微服务框架go-micro深度学习(二) 入门例子

        上一篇帖子简单介绍了go-micro的整体框架结构,这一篇主要写go-micro使用方式的例子,中间会穿插一些go-micro的源码,和调用流程图,帮大...

    lpxxn

扫码关注腾讯云开发者

领取腾讯云代金券