使用 golang 嵌套结构(带接口)
Using golang nesting structs (with interfaces)
用两个词来说,对于任何人来说,对于一个好的架构,我们应该使用接口,这对任何人来说都不是秘密,接口描述了它的行为。 Golang 实现了这个想法,但是他们的接口只有方法,没有字段。因此,使用它们的唯一方法就像创建 getter 和 setter。但是我遇到了指针问题。
例如:
package main
import "fmt"
// A
type IA interface {
Foo() string
}
type A struct {
foo string
}
func (a *A) Foo() string {
return a.foo
}
// B
type IB interface {
A() *IA
}
type B struct {
a *IA
}
func (b *B) A() *IA {
return b.a
}
// main
func main() {
a := &A{"lol"}
b := &B{a} // cannot use a (type *A) as type *IA in field value: *IA is pointer to interface, not interface
foo := b.A().Foo() // b.A().Foo undefined (type *IA is pointer to interface, not interface)
fmt.Println(foo)
}
Ofc,我可以这样使用:
(*(*b).A()).Foo()
但这就那么好和合适吗?
我只想拥有 python、js、ts 中的行为:
someObj.child1.child2.child2SomeMethod()
也许我搞砸了指针,我只是想知道 golang 处理嵌套对象的方式。
这是一个容易被绊倒的常见点,尤其是对于具有高级语言背景的新手来说。以下是我保持直率的方法:
首先,让我们确定一下Go中的“接收者”(例如“方法”)是什么。与 python 非常相似,方法实际上连接到类型这一事实是语法糖。考虑这个例子:
package main
import "fmt"
type F struct {
i int
}
func (f F)Foo() {
fmt.Println(f.i)
}
func main() {
F.Foo(F{1})
}
虽然令人惊讶,但这段代码编译并成功打印出预期的 1
。那是因为当你在类型上调用接收者时真正发生的是类型成为接收者的第一个参数。
真的很快让我们也回顾一下指针,正如你在评论中所说的那样。所以要明确一点:计算机程序中的任何值都存储在内存中,它在内存中的地址也是一个可以存储的值在一个变量中。我们称这些变量为值的“指针”。
如果我给一个函数传递一个值,他们只能在他们的函数范围内改变那个值。如果该值是内存中的地址,则同样如此。但是,我可以更改该地址 中的数据 ,从而影响函数范围之外的数据。
package main
import "fmt"
func f(i int) { i = i + 2 }
func pf(i *int) { *i = *i + 2 }
var i = 1
func main() {
f(i)
fmt.Println(i)
pf(&i)
fmt.Println(i)
}
打印出来
1
3
f(i)
更改了 i
的本地副本,但 pf(&i)
更改了 地址 中存储的数据 i
.
为什么我经历了这一切?因为这就是 most 接收器是指针接收器的原因;因为您不想传递接收者的 copy;你实际上想传递接收者的地址,这样它就可以在自己的方法中改变自己。
记住接收器是语法糖:
func (t *Type)f(arg string)
相当于:
func f(t *Type, arg string)
希望这能说明为什么指针接收器如此普遍!好的,关于界面方面的事情。
如您所知,一个接口定义了一组方法。如果一个类型定义了具有相同签名的方法,那么它就满足该接口。这可能适用于值接收者 或 指针接收者。
但是,一个类型不能同时具有同名的值和指针接收器:
func (t T)F() {}
func (t *T)F() {} //method redeclared: T.F
所以这意味着一个类型和一个指向该类型的指针不能有相同的接收者;因此 类型或指向该类型的指针实现接收器,但不能同时实现两者 。这一点很容易被忽略,因为go会自动转换。所以这很好用:
type T struct{}
func (t T)F() {}
func (t *T)PF() {}
func main() {
var t T
t.F()
t.PF()
}
在t.PF()
中,t
自动转换为指针。
但重申一下,类型和指向类型的指针不能同时定义相同的接收者。因此,如果 T
满足接口 I
,*T
则不满足,反之亦然。
已经说过并理解了这一点,很容易得出一个简单的规则:当你想用指针满足接口时,永远不要使用指向接口的指针。
在您的代码中,*A
满足 IA
。所以你可以说你的 IA
是“really an *A. Thinking of it like this, you can see that taking the address of an IA that is actually an
*A` 没有任何意义。
综上所述,这里是定义两个接口并链接调用。请注意,虽然指向我的结构的指针可能是满足接口的值,但我永远不需要获取接口的地址。对于 IF
和 IG
接口的消费者来说,它们是类型还是指针接收器都无关紧要。
package main
import "fmt"
type IF interface {
G() IG
}
type F struct {
g IG
}
func (f *F)G() IG {
return f.g
}
type IG interface {
G()
}
type G struct{
i int
}
func (g *G)G() {
g.i++
fmt.Println("Gee, ", g.i)
}
func main() {
f := F{&G{1}}
f.G().G()
}
很少需要指向接口的指针,因此请确保您认为“接口满足指针”而不是“指向接口的指针”。
用两个词来说,对于任何人来说,对于一个好的架构,我们应该使用接口,这对任何人来说都不是秘密,接口描述了它的行为。 Golang 实现了这个想法,但是他们的接口只有方法,没有字段。因此,使用它们的唯一方法就像创建 getter 和 setter。但是我遇到了指针问题。
例如:
package main
import "fmt"
// A
type IA interface {
Foo() string
}
type A struct {
foo string
}
func (a *A) Foo() string {
return a.foo
}
// B
type IB interface {
A() *IA
}
type B struct {
a *IA
}
func (b *B) A() *IA {
return b.a
}
// main
func main() {
a := &A{"lol"}
b := &B{a} // cannot use a (type *A) as type *IA in field value: *IA is pointer to interface, not interface
foo := b.A().Foo() // b.A().Foo undefined (type *IA is pointer to interface, not interface)
fmt.Println(foo)
}
Ofc,我可以这样使用:
(*(*b).A()).Foo()
但这就那么好和合适吗?
我只想拥有 python、js、ts 中的行为:
someObj.child1.child2.child2SomeMethod()
也许我搞砸了指针,我只是想知道 golang 处理嵌套对象的方式。
这是一个容易被绊倒的常见点,尤其是对于具有高级语言背景的新手来说。以下是我保持直率的方法:
首先,让我们确定一下Go中的“接收者”(例如“方法”)是什么。与 python 非常相似,方法实际上连接到类型这一事实是语法糖。考虑这个例子:
package main
import "fmt"
type F struct {
i int
}
func (f F)Foo() {
fmt.Println(f.i)
}
func main() {
F.Foo(F{1})
}
虽然令人惊讶,但这段代码编译并成功打印出预期的 1
。那是因为当你在类型上调用接收者时真正发生的是类型成为接收者的第一个参数。
真的很快让我们也回顾一下指针,正如你在评论中所说的那样。所以要明确一点:计算机程序中的任何值都存储在内存中,它在内存中的地址也是一个可以存储的值在一个变量中。我们称这些变量为值的“指针”。
如果我给一个函数传递一个值,他们只能在他们的函数范围内改变那个值。如果该值是内存中的地址,则同样如此。但是,我可以更改该地址 中的数据 ,从而影响函数范围之外的数据。
package main
import "fmt"
func f(i int) { i = i + 2 }
func pf(i *int) { *i = *i + 2 }
var i = 1
func main() {
f(i)
fmt.Println(i)
pf(&i)
fmt.Println(i)
}
打印出来
1
3
f(i)
更改了 i
的本地副本,但 pf(&i)
更改了 地址 中存储的数据 i
.
为什么我经历了这一切?因为这就是 most 接收器是指针接收器的原因;因为您不想传递接收者的 copy;你实际上想传递接收者的地址,这样它就可以在自己的方法中改变自己。
记住接收器是语法糖:
func (t *Type)f(arg string)
相当于:
func f(t *Type, arg string)
希望这能说明为什么指针接收器如此普遍!好的,关于界面方面的事情。
如您所知,一个接口定义了一组方法。如果一个类型定义了具有相同签名的方法,那么它就满足该接口。这可能适用于值接收者 或 指针接收者。
但是,一个类型不能同时具有同名的值和指针接收器:
func (t T)F() {}
func (t *T)F() {} //method redeclared: T.F
所以这意味着一个类型和一个指向该类型的指针不能有相同的接收者;因此 类型或指向该类型的指针实现接收器,但不能同时实现两者 。这一点很容易被忽略,因为go会自动转换。所以这很好用:
type T struct{}
func (t T)F() {}
func (t *T)PF() {}
func main() {
var t T
t.F()
t.PF()
}
在t.PF()
中,t
自动转换为指针。
但重申一下,类型和指向类型的指针不能同时定义相同的接收者。因此,如果 T
满足接口 I
,*T
则不满足,反之亦然。
已经说过并理解了这一点,很容易得出一个简单的规则:当你想用指针满足接口时,永远不要使用指向接口的指针。
在您的代码中,*A
满足 IA
。所以你可以说你的 IA
是“really an *A. Thinking of it like this, you can see that taking the address of an IA that is actually an
*A` 没有任何意义。
综上所述,这里是定义两个接口并链接调用。请注意,虽然指向我的结构的指针可能是满足接口的值,但我永远不需要获取接口的地址。对于 IF
和 IG
接口的消费者来说,它们是类型还是指针接收器都无关紧要。
package main
import "fmt"
type IF interface {
G() IG
}
type F struct {
g IG
}
func (f *F)G() IG {
return f.g
}
type IG interface {
G()
}
type G struct{
i int
}
func (g *G)G() {
g.i++
fmt.Println("Gee, ", g.i)
}
func main() {
f := F{&G{1}}
f.G().G()
}
很少需要指向接口的指针,因此请确保您认为“接口满足指针”而不是“指向接口的指针”。