关于go语言的一些问题总结

前言

前段时间做项目的时候,遇到了一些关于go语言的一些疑问点。当时只是记录了下来,没有深究,这两天有空,打算把这些疑问点清理清理,总结总结。希望可以便人便己。
在这里插入图片描述


疑问


在这里插入图片描述

Q1:为何go中的channel在使用时不能向普通的struct一样直接定义,而必须使用make呢?

cha:=make(chan int)

A: 在go中int,float,bool,string等基本数据类型属于是值类型的;而slice,map,channel,指针等属于是引用类型的;对于后者来说,它指向的地址才是真正的数据(确切的说,大多数引用类型本身代表了一个维护“真正数据内容”的结构体),
在这里插入图片描述
对于这种引用数据类型来说,使用时需要先创建(创建这个维护了引用类型的“结构体”)
在这里插入图片描述

ps: 对于slice和map来说,其初始化还可以使用初始化创建的方式,如下图所示.

slice1:=[]string{
    
    }
map1:=map[int]string{
    
    }

Q2: go中的interface是不是有点cpp中多继承的味道呢?

A: 对于interface来说,它像是一个“协议”,是服务提供者和服务使用者事先定义好的一种规范(当然,对于调包侠来说大多是被动的接受这种规范),这种规范在具体表现上就是一组方法。
  在go语言中,interface有很多作用【4】,其中的一个作用就是用作多重继承。即一个struct可以实现多个interface,来达到类似多重继承的目的(正儿八经的说go是不支持继承的,它的继承更像是利用语法糖包装组合来实现的)。java中的interface也是类似的作用,因为java中不支持多重继承,避免了cpp中的菱形继承的问题。


在这里插入图片描述

Q3: 如下代码,go语言中的多返回值使得无法使用链式函数调用的机制,有点不爽?

stu.setName("stu01").setAge(18).print()

A: 对于使用多返回值的函数来说,是无法使用链式调用的(返回单个对象的函数或许可以使用)。按照我查的资料来说,这是由于go原本设计的就想让程序更简单(而不是向cpp那样链式调用的奇技淫巧),每一个函数的返回,程序员都应该认真对待,这样可以是代码变得更清晰(使用一个局部变量来存储链式调用中的函数返回虽然有点麻烦,但好像确实是更清晰了);
  另一方面,go语言为了支持一个函数可以同时返回正确的值和错误时的error,设计了函数可以返回多个值的特性。这在某方面也强调了go的设计者们认为大多数函数都应该返回一个error值,而程序员也应该认真对待这个error值。

  函数可以返回多个值这个特性的另一个好处是go不用向cpp,c这种函数单返回值的语言那样,如果业务逻辑确实要返回多个值的话,需要额外在定义一个返回的结构体或者在传参的时候使用指针(或者引用)类型。

  对于函数返回多个值,网上的意见褒贬不一,具体可以参见【5】【6】,这里就不赘述了。


在这里插入图片描述

Q4:go语言的循环引用问题?

A: 首先说明一点的是,go语言不支持循环引用,即在A模块中引用B,又在B模块中反过来引用A。 理论上是可以在编译机制上支持循环引用的,但是这样不好。根据Go 语言之父 Rob Pike的说法,循环引用的代码设计是不合理的,没有很好的考虑代码的结构;
没有支持循环引用,目的是迫使 Go 程序员更多的考虑程序的依赖关系,保持依赖关系图的整洁。

  如果存在循环引用,意味着模块和模块之间就存在相互的调用,随着项目的推进,模块之间的依赖关系会越来越多,最后导致两个模块耦合性变得越来越高,最初之间的界限变得越来越模糊,最后都耦合在一起,变得一团糟。

  同时,循环引用还会使编译起来变得复杂缓慢。

  所以在go语言中是不支持循环引用的。

  这里多说一点关于自己关于循环引用的理解,不仅go语言中不支持循环引用,cpp/java/python 也非常不提倡在编程时出现循环引用的情况(只不过go中是原生不支持,其他语言可能编译会通过)。原因和上面的类似,主要是防止模块设计的不合理,模块耦合性过高。


在这里插入图片描述

Q5:go语言中的隐式转换问题?

A: 为了语言的简单和避免不必要的隐患。go是不支持变量之间隐式转换的,也就是说如果变量之间的类型不一样,那么它们之间的转换必须要由开发者使用强制显示转换(当然实际底层类型相同的变量有些可以转换的,详见【11】,这里不讨论这个)。如下代码所示:

i:=10
f:=12.9
f = i   //wrong
f = (float64)i //ok

  由于不能隐式转换这个问题,导致有的时候在编程时遇到多类型的问题,尤其是不同类型之间的相互操作,就不是非常方便。

  这里需要提醒一下,go中虽然不支持变量之间的隐式转换,但是一些有关常量的隐式转换还是支持的。详见【12】和【11】。


参考

【1】、golang中分为值类型和引用类型
【2】、make 和 new
【3】、Go语言创建切片
【4】、深入理解 Go Interface
【5】、函数/方法有多返回值到底科不科学?
【6】、王垠:Go语言野心勃勃,实际情况又如何
【7】、golang 函数多返回值下如何进行链式调用?
【8】、GO成神之路: Go异常处理(一)
【9】、在开发大C++工程的时候如何判断和避免循环include?
【10】、为什么 go 不支持循环引用
【11】、类型转换、赋值和值比较规则大全
【12】、golang快速入门[8.4]-常量与隐式类型转换

猜你喜欢

转载自blog.csdn.net/plm199513100/article/details/122453249