本文即Go语言的那些坑 二。
Golang中函数被看做是值,函数值不可以比较,也不可以作为map的key 请问以下代码能编译通过吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import ( "fmt" ) func main () { array := make (map [int ]func () int ) array[func () int { return 10 }()] = func () int { return 12 } fmt.Println(array) }`` ` **答案:**
可以正常编译通过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 稍作改动,改为如下的情况,还能编译通过吗? ```Go import ( "fmt" ) func main(){ array := make(map[func ()int]int) array[func()int{return 12}] = 10 fmt.Println(array) }``` **答案:**
不能编译通过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 在Go语言中,函数被看做是第一类值:(first-class values):函数和其他值一样,可以被赋值,可以传递给函数,可以从函数返回。也可以被当做是一种“函数类型”。例如:有函数``func square(n int) int { return n * n }``,那么就可以赋值``f := square``,而且还可以``fmt.Println(f(3))``(将打印出“9”)。 Go语言函数有两点很特别: + 函数值类型不能作为map的key + 函数值之间不可以比较,函数值只可以和nil作比较,函数类型的零值是``nil`` # 匿名函数作用域陷阱 请看下列代码输出什么? ```Go import ( "fmt" ) func main(){ var msgs []func() array := []string{ "1", "2", "3", "4", } for _, e := range array{ msgs = append(msgs, func(){ fmt.Println(e) }) } for _, v := range msgs{ v() } }
答案:
在上述代码中,匿名函数中记录的是循环变量的内存地址,而不是循环变量某一时刻的值。
想要输出1、2、3、4需要改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import ( "fmt" ) func main () { var msgs []func () array := []string { "1" , "2" , "3" , "4" , } for _, e := range array{ elem := e msgs = append (msgs, func () { fmt.Println(elem) }) } for _, v := range msgs{ v() } }
其实就加了条elem := e
看似多余,其实不,这样一来,每次循环后每个匿名函数中保存的就都是当时局部变量elem
的值,这样的局部变量定义了4个,每次循环生成一个。
[3]int
和 [4]int
不算同一个类型请看一下代码,请问输出true
还是false
1 2 3 4 5 6 7 8 9 10 11 import ( "fmt" "reflect" ) func main () { arrayA := [...]int {1 , 2 , 3 } arrayB := [...]int {1 , 2 , 3 , 4 } fmt.Println(reflect.TypeOf(arrayA) == reflect.TypeOf(arrayB)) }
答案是:
数组长度是数组类型的一个组成部分,因此[3]int和[4]int是两种不同的数组类型。
数组还可以指定一个索引和对应值的方式来初始化。 例如:
1 2 3 4 5 6 7 8 import ( "fmt" ) func main () { arrayA := [...]int {0 :1 , 2 :1 , 3 :4 } fmt.Println(arrayA) }
会输出:
有点像PHP
数组的感觉,但是又不一样:arrayA
的长度是多少呢?
1 2 3 4 5 6 7 8 import ( "fmt" ) func main () { arrayA := [...]int {0 :1 , 2 :1 , 3 :4 } fmt.Println(len (arrayA)) }
答案是:
没错,定义了一个数组长度为4的数组,指定索引的数组长度和最后一个索引的数值相关,例如:r := [...]int{99:-1}
就定义了一个含有100个元素的数组r
,最后一个元素输出化为-1,其他的元素都是用0初始化。
不能对map中的某个元素进行取地址&
操作
map中的元素不是一个变量,不能对map的元素进行取地址操作,禁止对map进行取地址操作的原因可能是map随着元素的增加map可能会重新分配内存空间,这样会导致原来的地址无效
当map为nil的时候,不能添加值 1 2 3 4 5 func main () { var sampleMap map [string ]int sampleMap["test" ] = 1 fmt.Println(sampleMap) }
输出报错:
1 panic: assignment to entry in nil map
必须使用make或者将map初始化之后,才可以添加元素。
以上代码可以改为:
1 2 3 4 5 6 7 8 func main () { var sampleMap map [string ]int sampleMap = map [string ]int { "test1" :1 , } sampleMap["test" ] = 1 fmt.Println(sampleMap) }
可以正确输出:
&dilbert.Position
和(&dilbert).Position
是不同的&dilbert.Position
相当于&(dilbert.Position)
而非(&dilbert).Position
请看例子:
请问输出什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func main () { type Employee struct { ID int Name string Address string DoB time.Time Position string Salary int ManagerID int } var dilbert Employee dilbert.Position = "123" position := &dilbert.Position fmt.Println(position) }
输出:
输出的是内存地址
修改一下,把&dilbert.Position
改为(&dilbert).Position
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func main () { type Employee struct { ID int Name string Address string DoB time.Time Position string Salary int ManagerID int } var dilbert Employee dilbert.Position = "123" position := &dilbert.Position fmt.Println(position) }
输出:
Go语言中函数返回的是值的时候,不能赋值 请看下面例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type Employee struct { ID int Name string Address string DoB time.Time Position string Salary int ManagerID int } func EmployeeByID (id int ) Employee { return Employee{ID:id} } func main () { EmployeeByID(1 ).Salary = 0 }
请问能编译通过吗?
运行,输出报错:cannot assign to EmployeeByID(1).Salary
在本例子中,函数EmployeeById(id int)
返回的是值类型的,它的取值EmployeeByID(1).Salary
也是一个值类型;值类型是什么概念?值类型就是和赋值语句var a = 1
或var a = hello world
等号=
右边的1
、Hello world
是一个概念,他是不能够被赋值的,只有变量能够被赋值。
修改程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type Employee struct { ID int Name string Address string DoB time.Time Position string Salary int ManagerID int } func EmployeeByID (id int ) Employee { return Employee{ID:id} } func main () { var a = EmployeeByID(1 ) a.Salary = 0 }
这就可以编译通过了
在声明方法时,如果一个类型名称本身就是一个指针的话,不允许出现在方法的接收器中 请看下面的例子,请问会编译通过吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import ( "fmt" ) type littleGirl struct { Name string Age int } type girl *littleGirlfunc (this girl) changeName(name string ){ this.Name = name } func main () { littleGirl := girl{Name:"Rose" , Age:1 } girl.changeName("yoyo" ) fmt.Println(littleGirl) }
答案:
1 不能编译通过,会提示“invalid receiver type girl(girl is a pointer type)”
Go语言中规定,只有类型(Type)和指向他们的指针(*Type)才是可能会出现在接收器声明里的两种接收器,为了避免歧义,明确规定,如果一个类型名本身就是一个指针的话,是不允许出现在接收器中的。
函数允许nil指针作为参数,也允许用nil作为方法的接收器 请看下面的例子,请问能编译通过吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import ( "fmt" ) type littleGirl struct { Name string Age int } func (this littleGirl) changeName(name string ){ fmt.Println(name) } func main () { little := littleGirl{Name:"Rose" , Age:1 } little = nil little.changeName("yoyo" ) fmt.Println(little) }
答案:
1 不能编译通过,显示"cannot use nil as type littleGirl in assignment"
Go语言中,允许方法用nil指针作为其接收器,也允许函数将nil指针作为参数。而上述代码中的littleGirl
不是指针类型,改为*littleGirl
,然后变量little
赋值为&littleGirl{Name:"Rose", Age:1}
就可以编译通过了。 并且,nil对于对象来说是合法的零值的时候,比如map或者slice,也可以编译通过并正常运行。
Golang的时间格式化 不同于PHP的date("Y-m-d H:i:s", time())
,Golang的格式化奇葩的很,不能使用诸如Y-m-d H:i:s
的东西,而是使用2006-01-02 15:04:05
这个时间的格式,请记住这个时间,据说这是Golang的诞生时间。
1 2 3 4 5 6 7 8 9 time := time.Now() time.Format("20060102" ) time.Format("2006-01-02" ) time.Format("2006-01-02 15:04:05" ) time.Format("2006-01-02 00:00:00" )