社区编辑申请
注册/登录
Go 语言里怎么正确实现枚举?答案藏着官方的源码里
开发 后端
在编程领域里,枚举是用来表示只包含有限数量的固定值的类型,在开发中一般用于标识错误码或者状态机。拿一个实体对象的状态机来说,它通常与这个对象在数据库里对应记录的标识状态的字段值相对应。

本文转载自微信公众号「网管叨bi叨」,作者网管。转载本文请联系网管叨bi叨公众号。

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

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

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

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

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

  1. public class ArticleState { 
  2.      
  3.     public static final int Draft = 1; //草稿 
  4.      
  5.     public static final int Published = 2; //发布 
  6.      
  7.     public static final int Deleted = 3; // 已删除 
  8.  
  9. public  Boolean checkArticleState(int state) { 
  10.      
  11.     ... 
  12.      

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

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

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

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

  1. // 使用enum而非class声明 
  2. public enum ArticleState { 
  3.   
  4.     //要在enum里创建所有的枚举对象 
  5.     Draft(1, "草稿"); 
  6.     Published(2, "已发布"); 
  7.     Deleted(3, "已删除"
  8.        
  9.     // 自定义属性 
  10.     private int code; 
  11.  
  12.     private String text; 
  13.    
  14.     // 构造方法必须是private的 
  15.     ArticleState(int code, String text) { 
  16.         this.code = id; 
  17.         this.text = name
  18.     } 
  19.  
  20. public  Boolean checkArticleState(ArticleState state) { 
  21.      
  22.     ... 
  23.      

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

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

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

  1. const ( 
  2.     Draft int = 1 
  3.     Published = 2 
  4.     Deleted   = 3 
  5.  
  6. const ( 
  7.     Summer int = 1 
  8.     Autumn     = 2 
  9.     Winter     = 3 
  10.     Spring     = 4 
  11.  
  12. func main() { 
  13.     // 输出 true, 不会有任何编译错误 
  14.     fmt.Println(Autumn == Draft) 

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

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

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

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

  1. type Season int 
  2.  
  3. const ( 
  4.  Summer Season = 1 
  5.  Autumn        = 2 
  6.  Winter        = 3 
  7.  Spring        = 4 

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

  1. type Season int 
  2.  
  3. const ( 
  4.  Summer Season = iota + 1 
  5.  Autumn 
  6.  Winter 
  7.  Spring 
  8.  
  9. type ArticleState int 
  10.  
  11. const ( 
  12.   Draft ArticleState = iota + 1 
  13.   Published 
  14.   Deleted   
  15.  
  16. func checkArticleState(ArticleState state) { 
  17.  // ...  
  18.  
  19.  func main() { 
  20.    // 两个操作数类型不匹配,编译错误 
  21.    fmt.Println(Autumn == Draft) 
  22.    // 参数类型不匹配,编译错误 
  23.    checkArticleState(100) 
  24.  } 

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

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

 

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

 

责任编辑:武晓燕 来源: 网管叨bi叨
相关推荐

2022-04-11 11:38:44

Python代码游戏

2022-05-07 10:09:01

开发Java日志

2022-05-03 22:25:57

Python浏览器语言

2022-05-16 10:36:08

GitHub开源项目

2022-05-17 16:56:33

开发工具前端

2022-04-18 09:41:14

Go架构设计

2022-04-13 08:20:32

DockerGo项目

2022-05-10 16:04:40

编程语言PythonC语言

2022-05-16 10:49:28

网络协议数据

2022-04-13 09:57:24

Go语言C语言程序开发

2022-03-25 21:57:49

2022-02-21 18:16:38

Go语言枚举

2022-04-01 15:18:04

HarmonyHDF 驱动鸿蒙

2022-05-06 16:31:27

人工智能自然语言生物特征识别

2022-05-18 08:05:31

2022-05-09 07:08:14

LinuxBashShell

2022-04-10 23:02:08

GoRust语言

2022-04-22 14:15:59

Go开发者受访者

2022-04-12 11:20:11

C 语言Linux编程

2022-03-11 10:53:32

UML建模语言

同话题下的热门内容

Python 字符串总结,建议收藏!这份Java日志格式规范,拿走不谢!Mybatis-Plus官方发布分库分表神器,一个依赖轻松搞定!后端思维篇:如何应用设计模式优化代码改变 Python 对象规则的黑魔法 Metaclass几种限流算法的Go语言实现JMeter关联之正则表达式提取器在 Go 中实现一个支持并发的 TCP 服务端

编辑推荐

使用Kotlin做开发一个月后的感想面试官问你什么是消息队列?把这篇甩给他!五大自动化测试的Python框架图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)2018年最流行的十大编程语言,其中包括你用的语言吗?
我收藏的内容
点赞
收藏

51CTO技术栈公众号