0%

goland提取方法与...interface{}类型的注意点

本文记录了作者在使用goland提供的提取方法(Extract Method)功能时,由于...interface{}的类型问题而遭遇到的bug和一点感悟,简单来说:

  • 可变长的参数会被goland的Extract Method转化为切片(slice),比如...interface{}会被转化为[]interface{}
  • 由于interface{}的特殊性,需要额外注意

bug的产生

在代码中有如下片段

1
2
3
4
5
func f(payload ...interface{}) {
for _, p := range payload {
// do something
}
}

现在想要将循环提取出来,作为一个方法,在goland中可以直接选中文本然后Extract Method,但是结果是这样的

1
2
3
4
5
6
7
8
9
func f(payload ...interface{}) {
ExtractedMethod(payload)
}

func ExtractedMethod(payload []interface{}) {
for _, p := range payload {
// do something
}
}

期望中的函数签名是ExtractedMethod(payload ...interface{}),不符合预期,所以要进行修改

1
2
3
4
5
6
7
8
9
10
11
// WRONG!!!!!!!!

func f(payload ...interface{}) {
ExtractedMethod(payload)
}

func ExtractedMethod(payload ...interface{}) {
for _, p := range payload {
// do something
}
}

上述的代码不会有编译错误,但是是完全不符合预期的,为什么呢?

bug的原因

1
2
3
4
5
6
7
8
9
10
11
12
// WRONG!!!!!!!!

func f(payload ...interface{}) {
// we need to unpack payload
ExtractedMethod(payload)
}

func ExtractedMethod(payload ...interface{}) {
for _, p := range payload {
// do something
}
}

如上述注释所说,我们需要对f中传递给ExtractedMethod的参数payload做一个解包工作,因为

  • 我们f函数的本意是要用ExtractedMehtodpayload中的每一个元素做处理
  • 现在不解包,payload原本一个[]interface{}又被额外自动包装了一层,成为了interface{}传递给了ExtractedMethod,只会对整个payload做一次处理

bug的解决与思考

将代码修改为如下后正确

1
2
3
4
5
6
7
8
9
func f(payload ...interface{}) {
ExtractedMethod(payload...)
}

func ExtractedMethod(payload ...interface{}) {
for _, p := range payload {
// do something
}
}

以后可以采取的方法是,先将传参的地方payload改为payload...,这样的话如果忘记修改参数[]interface{}...interface{},是会有编译器报错的

这个修改顺序可以让编译器为我们保驾