Golang中如何实现枚举详析

Golang中怎么实现枚举?下面本篇文章给大家介绍一下Golang中实现枚举的方法,希望对大家有所帮助!

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

在编程领域里,枚举用来表示只包含有限数量的固定值的类型,在开发中一般用于标识错误码或者状态机。拿一个实体对象的状态机来说,它通常与这个对象在数据库里对应记录的标识状态的字段值相对应。【相关推荐: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 int = iota + 1
  Published
  Deleted 
)

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

 func main() {
   // 两个操作数类型不匹配,编译错误
   fmt.Println(Autumn == Draft)
    // 参数类型不匹配,但是因为 ArticleState 底层的类型是 int 所以传递 int 的时候会发生隐式类型转换,所以不会报错
   checkArticleState(100)
 }

虽然这些状态值的底层的类型都是 int 值,但是现在进行两个不相干类型的枚举值比较,会造成编译错误,因为现在我们使用状态值的地方都有了类型限制。

不过函数 checkArticleState 的参数类型设置成了 ArticleState 但是因为 ArticleState 底层的类型是 int 。所以调用 checkArticleState 时传递 int 类型的参数会发生隐式类型转换,不会造成编译报错,这块如果想解决,只能重新定义类型来实现了

总结

到此这篇关于Golang中如何实现枚举的文章就介绍到这了,更多相关Golang实现枚举内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Golang表示枚举类型的详细讲解

    枚举,是一种重要的数据类型,由一组键值对组成,通常用来在编程语言中充当常量的标识符.在主流行编程语言如 c. java 等,都有原生支持.在 go 中,大家却找不到 enum 或者其它直接用来声明枚举类型的关键字.从熟悉其它编程语言的开发者转用 go 编程,刚开始会比较难接受这种情况.其实,如果你看到如何在 go 中表示枚举类型时,可能会感受到 go 语言设计者对简洁性.问题考虑的深度,是一般资浅工程师无法比拟的. 其实,在 go 语言设计者的眼里,enum 本质是常量,为什么要多余一个关键字呢

  • Golang中如何实现枚举详析

    Golang中怎么实现枚举?下面本篇文章给大家介绍一下Golang中实现枚举的方法,希望对大家有所帮助! 在编程领域里,枚举用来表示只包含有限数量的固定值的类型,在开发中一般用于标识错误码或者状态机.拿一个实体对象的状态机来说,它通常与这个对象在数据库里对应记录的标识状态的字段值相对应. 在编程领域里,枚举用来表示只包含有限数量的固定值的类型,在开发中一般用于标识错误码或者状态机.拿一个实体对象的状态机来说,它通常与这个对象在数据库里对应记录的标识状态的字段值相对应.[相关推荐:Go视频教程]

  • 深入Golang中的sync.Pool详解

    我们通常用golang来构建高并发场景下的应用,但是由于golang内建的GC机制会影响应用的性能,为了减少GC,golang提供了对象重用的机制,也就是sync.Pool对象池. sync.Pool是可伸缩的,并发安全的.其大小仅受限于内存的大小,可以被看作是一个存放可重用对象的值的容器. 设计的目的是存放已经分配的但是暂时不用的对象,在需要用到的时候直接从pool中取. 任何存放区其中的值可以在任何时候被删除而不通知,在高负载下可以动态的扩容,在不活跃时对象池会收缩. sync.Pool首先

  • Spring Boot 中starter的原理详析

    目录 1.springboot 的starter 的启动原理是什么 原理 来个例子 小结 2.springboot 是如何找到配置类的 3.springboot starter 的bean 是怎么加载到容器的 4.总结 前言: 今天介绍springboot ,也是写下springboot的插件机制,starter的原理,其实这个网上已经很多了,也是看了不少别人的文章,今天主要还是带着问题去记录下. 1.springboot 的starter 的启动原理是什么 原理 这个问题是很简单的,只要了解s

  • golang中context的作用详解

    当一个goroutine可以启动其他goroutine,而这些goroutine可以启动其他goroutine,依此类推,则第一个goroutine应该能够向所有其它goroutine发送取消信号. 上下文包的唯一目的是在goroutine之间执行取消信号,而不管它们如何生成.上下文的接口定义为: type Context interface { Deadline() (deadline time.Time, ok bool) Done() <- chan struct{} Err() erro

  • golang中的nil接收器详解

    我们先看一个简单的例子,我们自定义一个错误,用来把多个错误放在一起输出: type CustomError struct {errors []string} func (c *CustomError) Add(err string) {c.errors = append(c.errors, err)} func (c *CustomError) Error() string {return strings.Join(c.errors, ";")} 因为实现了Error() string

  • Golang中的参数传递示例详解

    前言 本文主要给大家介绍了关于Golang参数传递的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 关于参数传递,Golang文档中有这么一句: after they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. 函数调用参数均为值传递,不是指针传递或引用传递.经测试引申出来,

  • Swift中的限定扩展详析

    前言 现在很多公司的iOS新项目都开始用Swift来代替OC开发了,Swift带来的亮点和新功能很多,但我觉得最重要的一点是引导我们编程思想的改变,将我们在OC中用到的传统的面向对象编程思想OOP(object-oriented programming)向面向协议编程思想POP(protocol oriented programming)以及面向值的编程思想VOP(value-oriented programming)上转变,苹果也让我们开发者在编程的时候"从一个protocol开始,别从一个c

  • Golang中runtime的使用详解

    runtime 调度器是个非常有用的东西,关于 runtime 包几个方法: Gosched:让当前线程让出 cpu 以让其它线程运行,它不会挂起当前线程,因此当前线程未来会继续执行 NumCPU:返回当前系统的 CPU 核数量 GOMAXPROCS:设置最大的可同时使用的 CPU 核数 Goexit:退出当前 goroutine(但是defer语句会照常执行) NumGoroutine:返回正在执行和排队的任务总数 GOOS:目标操作系统 NumCPU package main import

  • Golang中定时器的陷阱详解

    前言 在业务中,我们经常需要基于定时任务来触发来实现各种功能.比如TTL会话管理.锁.定时任务(闹钟)或更复杂的状态切换等等.百纳网主要给大家介绍了关于Golang定时器陷阱的相关内容,所谓陷阱,就是它不是你认为的那样,这种认知误差可能让你的软件留下隐藏Bug.刚好Timer就有3个陷阱,我们会讲 1)Reset的陷阱和 2)通道的陷阱, 3)Stop的陷阱与Reset的陷阱类似,自己探索吧. 下面话不多说了,来一起看看详细的介绍吧 Reset的陷阱在哪 Timer.Reset()函数的返回值是

  • Golang中的路由使用详解

    之前有篇文章比较浅显的分析了一下golang的服务器如何实现,还有Handler, DefaultServeMux,HandlerFunc的用处. 我们现在已经明白了DefaultServeMux就是存放pattern和handler的地方,我们称其为路由,那么我们可能会想,既然golang能够实现这个路由,我们能否也模仿一个呢? 首先我们需要一个能够保存客户端的请求的一个容器(路由). 创建路由结构体 type CopyRouter struct { router map[string]map

随机推荐