一起聊聊Go语言中的语法糖的使用

目录
  • 前言
  • 进入正题
    • 可变长参数
    • 声明不定长数组
    • ... 操作符
    • 切片循环
    • 忽略变量、字段或者导包
    • 短变量声明
    • 另类的返回值
  • 总结

前言

由于工作变动,我现在已经开始使用Golang了。用了一段时间之后,我发现Golang(后面简称Go)中的语法糖还蛮多的,有些语法糖还让会让人很懵逼。那么接下来,让我以一个曾经的 Java CURD boy,来说一说 Go 中的语法糖。

进入正题

至于什么是语法糖,名词解释我就不解释了,老司机自然是懂,新手司机想了解的可以去百度问一下。闲话少说我们直接开讲。

可变长参数

Go语言允许一个函数把任意数量的值作为参数,Go语言内置了... 操作符,在函数的最后一个形参才能使用...操作符,使用它必须注意如下事项

  • 可变长参数必须在函数列表的最后一个;
  • 把可变长参数当 切片来解析,可变长参数没有没有值时就是个空切片
  • 可变长参数的类型必须相同
func test(a int, b ...int) {
	fmt.Println("a=", a, ",b=", b, ",b的类型=", reflect.TypeOf(b))
	return
}

输出结果如下:

a= 1 ,b= [] ,b的类型= []int

为啥说可变长参数的值用切片来解析,而不是数组。为什么是这样有兴趣的朋友可以思考一下

可变长参数这个语法糖,不是Go独有的,Java中也有,不同的是Java是通过数组实现此语法糖的。从实际开发经验来看,这个语法糖我在使用Java开发时,貌似一次都没有用过,用Go开发的时候我用的次数还挺多的,具体在什么地方用,后面有机会我再说说它是如何使用的。

声明不定长数组

我么都知道数组长度是固定的,所以在声明数组的时候都要指定长度,Go里提供了一种偷懒的声明方式,即使用...操作符声明数组时,我们只管填充元素值,其他的由Go编译器来处理。

// Go的实现:数组长度是4,等同于 a := [4]{1, 2, 3, 4}
a := [...]int{1, 2, 3, 4}

这个Java中有实现,而且感觉比Go的还简单,具体如下:

// Java的实现:数组长度是4
int[] x = {1,2,3,4};

在我短暂的职业生涯中,无论我使用Java还是Go开发的时候,数组使用的频率都是比较少的。

ps 我发现这个...好像也算是一个语法糖

... 操作符

...这个叫啥名字,我也没有找到官方的叫法。但是我发现在Go实际的开发过程中用的地方还蛮多的。

  • 函数的参数声明。 如:func funcName(nums ...int),在函数的方法体内,nums作为一个切片[]int来使用,这个上面已经提到了。
  • 传参时列表打散。 如:params = []int{1,2,3},调用某个有三个参数的方法 func ThreeParamFunc(a, b, c int)时可以ThreeParamFunc(params...)。三个点...在JavaScript中的名叫扩展运算符,是在ES6中新增加的内容,它可以在函数调用/数组构造时,将数组表达式或者string在语法层面展开;还可以在构造字面量对象时将对象表达式按照key-value的方式展开,例如:
// 数组
var number = [1,2,3,4,5,6]
console.log(...number) //1 2 3 4 5 6
//对象
var man = {name:'蔡',height:180}
console.log({...man}) / {name:'蔡',height:180}

所以我觉得在Go里面在这种情况下,我们也可以称...为扩展运算符。

  • 声明不定长数组。 如果元素指定,那么可以不必显式声明数组长度,可以根据元素个数推断,如:arr := [...]int{1,2,3} ,这个上面已经提到了。
  • 在 go 命令行中,被当做包列表的通配符。如:$ go test ./... 这条命令会执行当前目录及子目录下的所有包测试文件。

切片循环

在Go中提供了for range语法来快速迭代对象。数组、切片、字符串、map、channel等等类型都可以使用这种方式进行遍历,总结起来有以下几种形式:

只遍历不关心数据,适用于切片、数组、字符串、map、channel

for range T {}

遍历获取索引或数组,切片,数组、字符串就是索引,map就是key,channel就是数据

for key := range T{}

遍历获取索引和数据,适用于切片、数组、字符串,第一个参数就是索引,第二个参数就是对应的元素值,map 第一个参数就是key,第二个参数就是对应的值;

for key, value := range T{}

其实在实际开发中,我们会大概率会遇到遍历map时,只关心map中的数据,不关心key的情况。这个时候我们就是使用最后一种方式,这个key声明了但是没有用,Go这个时候就会提示一个语法错误key没有使用,那我们只好使用Go的另外一个语法糖_忽略标识符(就是一个下划线)忽略key,具体如下:

for _, value := range T{}

在Java中循环map的方式有很多种,但有一点就是,开发者可以使用keySet()values()选择遍历key或者value。

// 打印键集合
for (String key : map.keySet()) {
    System.out.println(key);
}
// 打印值集合
for (String value : map.values()) {
    System.out.println(value);
}

另外注意一点,在Go中如果一个切片是nil的时候,我们对他进行遍历或者append操作的时候,是不会出现报错的,这一点很不错,省的像用Java时遍历对象需要判断他是否为null。

func main() {

	temp := make([]int, 0)
	temp = nil

	for _, val := range temp {
		fmt.Println("val=", val)
	}
	temp = append(temp, 1)
	fmt.Println("val=", temp)
}

上述操作都是不会报错的,大家放心食用!

忽略变量、字段或者导包

这个前面提到了一点,使用_忽略变量。在Go中还有其他几种常见的场景,具体如下:

json序列化忽略某个字段 我们都会对struct做序列化操作,但有些时候我们想要json里面的某些字段不参加序列化,Go语言的结构体提供标签功能,在结构体标签中使用 - 操作符就可以对不需要序列化的字段做特殊处理,使用如下:

type Item struct{
  Id uint32 `json:"id"`
  Name string `json: "name"`
  Password string `json: "-"`
}

这个Java中也有类似的实现,只要在Java类的属性前加上transient关键字修饰即可。当然在将Java类序列化成json时可以使用对应的注解,这里我就不细说了。

json序列化忽略空值字段 使用json.Marshal进行序列化时不会忽略struct中的空值(这里说的空值包含空字符串和nil),默认输出字段的类型零值(string类型零值是"",指针类型的零值是nil),如果我们想在序列化时忽略掉这些没有值的字段时,可以在结构体标签中中添加omitempty tag。

type Item struct{
  Id uint32 `json:"id"`
  Name string `json: "name,omitempty"`
  Password string `json: "-"`
}

这里说一下,在Java里类型分为基本类型和包装类型,Java类初始化的时候属性为基本类型如果没有赋予初始值,默认值是0。包装类型声明时没有赋值的话的初始值为null。Go中初始化时没有赋值的变量的默认值如下:

  • 布尔类型的默认为false
  • 数值类型的默认为0
  • 字符串类型的默认为空字符串""
  • 指针类型、函数、接口、切片、通道和map默认值为nil

这样看来Java和Go这个场景下处理方式,有相似和不同之处,大家开发的时候要注意,由Java转Go的同学开发时,千万别搞混了。

短变量声明

在强类型语言中,声明一个变量都需要指定变量的类型。可能语言的开发者觉得这样做对开发者不太友好,就搞了个变量声明不用指定类型的语法糖,其实这个玩意说起来就是类型推导(Java8之后的版本貌似已经有了),开发者只管定义变量,类型由语言编译器来处理。

a := 10
#等用于
var a int = 10
#或者是
b:=fucName()

怎么说呢?这样有好处也有坏处,定义变量的人省事了,使用变量的人可能就懵逼了。就像这种场景b:=fucName(),这个 变量b是啥类型,这个时候你只能点击函数内,看函数的返回值类型是啥,才能确定变量b是啥类型。

我之前写过几年的PHP,后来转了Java,再到现在写Go。我发现各种开发语言都在进步,而且还相互模仿,PHP中函数之前不用指定形参类型,PHP8中好像可以指定形参类型了。总之就是强弱类型的语言在相互靠拢。

另类的返回值

在Go语言中,允许您使用return语句从一个函数返回多个值。换句话说,在函数中,单个return语句可以返回多个值。返回值的类型类似于参数列表中定义的参数的类型。

func func1(a string, b int) int {
   fmt.Println("func1------------")
   fmt.Println("a1 = ", a)
   fmt.Println("b1 = ", b)

   c := 100

   return c
}

可以这样写:返回多个返回值,形参命名

func func2(a string, b int) (int, int) {
	fmt.Println("func2------------")
	fmt.Println("a2 = ", a)
	fmt.Println("b2 = ", b)

	return 12, 33
}

可以这样写:返回多个返回值,形参匿名

func func3(a string, b int) (int, int) {
   fmt.Println("func3------------")
   fmt.Println("a2 = ", a)
   fmt.Println("b2 = ", b)

   return 12, 33
}

如果一个函数要返回多个值,在Java中可以使用定义一个新的类来承载返回值,或者偷个懒使用map来接也是可以的。go支持多个返回值就我个人来说还是支持的。其实说到这里,多个返回值的各种形式都能理解。直到有一天我在翻看gorm的Open方法源码发现了奇怪的地方,代码位置信息:gorm.io/gorm@v1.23.4/gorm.go:116 ,节选部分代码如下:

func Open(dialector Dialector, opts ...Option) (db *DB, err error) {
	config := &Config{}

if d, ok := dialector.(interface{ Apply(*Config) error }); ok {
		if err = d.Apply(config); err != nil {
			return
		}
	}
	# 省略此处无用代码
	db = &DB{Config: config, clone: 1}

	db.callbacks = initializeCallbacks(db)

    # 省略此处无用代码
	preparedStmt := &PreparedStmtDB{
		ConnPool:    db.ConnPool,
		Stmts:       map[string]Stmt{},
		Mux:         &sync.RWMutex{},
		PreparedSQL: make([]string, 0, 100),
	}
	db.cacheStore.Store(preparedStmtDBKey, preparedStmt)

    # 省略此处无用代码
	db.Statement = &Statement{
		DB:       db,
		ConnPool: db.ConnPool,
		Context:  context.Background(),
		Clauses:  map[string]clause.Clause{},
	}
    # 省略此处无用代码

	return
}

这就是文章开头提到的让人懵逼的语法糖,我当时看到这段代码时,我心中暗想这个是什么TM操作,竟然这样也行,这样竟然没有报错…… 我来点出其中的问题,就是return关键字处并没有返回dberror变量。我把上述代码在简化一下,用最简单的方式列出来,如下:

func func4(a string, b int) (r1 int, r2 int) {
	fmt.Println("func4------------")
	//r1 r2输入fool3的形参,初始化默认的值是0
	//r1 r2 作用域空间是 func4 整个函数体的{}空间
	fmt.Println("r1 = ", r1)
	fmt.Println("r2 = ", r2)
	r1 = b * 2
	r2 = 2000

	return
}

上述这种方式其实本质上来说是和前面几种方式一样,只是在不知道这种约定的话,会让人难以理解。知道这个算是Go中的小tips之后,咱也不知道Go为啥要这么做,只是觉得有点懵,我只是觉得在Java中绝对不会出现这种情况。但是在Go中也许是设计Go的大佬们觉得这样做可以省掉声明变量r1r2的时间,毕竟大佬们的时间都很宝贵。

总结

本文介绍了一些Go语言中的语法糖,当然并不全面,应该还有其他的没有介绍,希望大家能够看完本篇文章后,能了解并掌握,并能在实际开发中运用到,当然其中的函数多返回值的懵逼写法,就由大家自行判断是用还是不用了。

以上就是一起聊聊Go语言中的语法糖的使用的详细内容,更多关于Go语言 语法糖的资料请关注我们其它相关文章!

(0)

相关推荐

  • Go语法糖之‘...’ 的使用实例详解

    语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用.通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会. 下面通过例子看下Go语法糖之'...' 的使用,具体内容如下所示: '-' 其实是go的一种语法糖. 它的第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数. 第二个用法是sli

  • Go语言提升开发效率的语法糖技巧分享

    目录 前言 可变长参数 声明不定长数组 init函数 忽略导包 忽略字段 json序列化忽略某个字段 json序列化忽略空值字段 短变量声明 类型断言 切片循环 判断map的key是否存在 select控制结构 前言 哈喽,大家好,我是asong. 每门语言都有自己的语法糖,像java​的语法糖就有方法变长参数.拆箱与装箱.枚举.for-each​等等,Go​语言也不例外,其也有自己的语法糖,掌握这些语法糖可以助我们提高开发的效率,所以本文就来介绍一些Go语言的语法糖,总结的可能不能全,欢迎评论

  • 一起聊聊Go语言中的语法糖的使用

    目录 前言 进入正题 可变长参数 声明不定长数组 ... 操作符 切片循环 忽略变量.字段或者导包 短变量声明 另类的返回值 总结 前言 由于工作变动,我现在已经开始使用Golang了.用了一段时间之后,我发现Golang(后面简称Go)中的语法糖还蛮多的,有些语法糖还让会让人很懵逼.那么接下来,让我以一个曾经的 Java CURD boy,来说一说 Go 中的语法糖. 进入正题 至于什么是语法糖,名词解释我就不解释了,老司机自然是懂,新手司机想了解的可以去百度问一下.闲话少说我们直接开讲. 可

  • Java 中的语法糖,真甜

    我把自己以往的文章汇总成为了 Github ,欢迎各位大佬 star https://github.com/crisxuan/bestJavaer 我们在日常开发中经常会使用到诸如泛型.自动拆箱和装箱.内部类.增强 for 循环.try-with-resources 语法.lambda 表达式等,我们只觉得用的很爽,因为这些特性能够帮助我们减轻开发工作量:但我们未曾认真研究过这些特性的本质是什么,那么这篇文章,cxuan 就来为你揭开这些特性背后的真相. 语法糖 在聊之前我们需要先了解一下 语法

  • 详解Objective-C中的语法糖@{}究竟是什么

    最近在技术群里有一个群友提出了一个问题,就是为什么下面代码打印的结果不一样? NSMutableDictionary *mDic1 = [NSMutableDictionary dictionaryWithDictionary:@{@"a":@1, @"a":@2}]; //'a': 1 NSMutableDictionary *mDic2 = [NSMutableDictionary dictionary]; [mDic2 setObject:@(1) forKe

  • 一文搞懂Vue3.2中setup语法糖使用

    目录 前言 一.如何使用setup语法糖 二.data数据的使用 三.method方法的使用 四.watchEffect的使用 五.watch的使用 六.computed计算属性的使用 七.props父子传值的使用 八.emit子父传值的使用 九.获取子组件ref变量和defineExpose暴露 十.路由useRoute和useRouter的使用 十一.store仓库的使用 十二.await 的支持 十三.provide 和 inject 祖孙传值 前言 提示:Vue3.2 版本开始才能使用语

  • vue3中setup语法糖下通用的分页插件实例详解

    目录 vue3中setup语法糖下父子组件之间的通信 准备工作 父传子: 子传父: 先给大家介绍下vue3中setup语法糖下通用的分页插件,内容如下所示: 效果 自定义分页插件:PagePlugin.vue <script setup lang="ts"> // total :用来传递数据总条数 // pageSize :每页展示几条数据 // currentPage :当前默认页码 // change-page :页码改变时触发的事件,参数为当前页码 const pro

  • vue中setup语法糖写法实例

    目录 变量声明 父传子 子传父 变量声明 变量声明定义的时候,不需要返回可以直接使用即可 没有使用setup语法糖时写法 <script> import {useStore} from 'vuex' export default { setup() { const store=useStore() const plus=()=>{ store.commit('plus') } return {plus} }, } </script> 使用setup语法糖写法 <scri

  • Objective-C中的语法糖示例详解

    语法糖 语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用. --维基百科 需要声明的是"语法糖"这个词绝非贬义词,它可以给我带来方便,是一种便捷的写法,编译器会帮我们做转换:而且可以提高开发编码的效率. 通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会,本文在简单的介绍 OC 语法糖的同时也会

  • 详细聊聊c语言中的缓冲区问题

    目录 发现问题 例题 问题原因 解决方法一: 解决方法二: 解决方案三: 出错二 gets函数引入 为什么要引入缓冲区 总结 发现问题 你是不是总会出现当你输入的时候(你想的是只输出一个内容),但是最后却输入两个. 比如下面这个例子  那这到底是是哪出了问题呢? 没错这就是关于缓冲区的问题. 我们先仔细了解这个题目 例题 判断字母是否为元音字母包括大小写. 看代码实现(错误的) #include<stdio.h> int main() { int i = 0; char ch = 0; cha

  • 不了解这12个语法糖,别说你会Java

    本文从 Java 编译原理角度,深入字节码及 class 文件,抽丝剥茧,了解 Java 中的语法糖原理及用法,帮助大家在学会如何使用 Java 语法糖的同时,了解这些语法糖背后的原理. 语法糖 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用.简而言之,语法糖让程序更加简洁,有更高的可读性. 有意思的是,在编程领域,除了语法糖,还有语法盐

  • 强烈推荐!Vue3.2中的setup语法糖

    目录 前文 1.什么是setup语法糖 2.使用setup组件自动注册 3.使用setup后新增API 3.1 defineProps 3.2 defineEmits 3.3 defineExpose vue3项目如何开启setup语法糖 总结: 前文 作为一个前端程序员,说起 Vue 3肯定不会陌生,作为时下最火的前端框架之一,很多人将它作为入门框架. 但是尽管 Vue 3很久之前就已经开始投入使用,也不免会有人抱怨 Vue 3的知识点太多太杂,更新太快.这不,最近 Vue 3又定稿了一项新技

随机推荐