一、接口
1.1、定義接口
// notifier 是一個定義了通知類行為的接口
type notifier interface {
// 接口方法
notify()
}
1.2、實現(xiàn)接口
使用值接收者實現(xiàn)接口
// notify 是使用值接收者實現(xiàn) notifier interface 接口的方法
// sendNotification(&u) 和 sendNotification(u) 都可
func (u user) notify() {
fmt.Println("notify", u)
}
- 對于值接收者,sendNotification(
&u) 和 sendNotification(u) 都可
使用指針接收者實現(xiàn)接口
// notify 是使用指針接收者實現(xiàn) notifier interface 接口的方法
// 只能使用 sendNotification(&u)
func (u *user) notify() {
fmt.Println("notify", *u)
}
- 對于指針接收者,只能使用 sendNotification(
&u)
非常重要的點 - 值接收者與指針接收的對比
type User struct {
Name string
Email string
}
// 注意這個是值接收者
func (u User) Notify() error {
u.Name = "alimon"
return nil
}
func main() {
u := &User{"Damon", "damon@xxoo.com"}
u.Notify()
log.Println(u.Name)
}
注意
- 值接收者(func
(u User)Notify() error)操作的是User 的副本(不管調(diào)用者使用值還是地址),所以不會改變其內(nèi)部的值,這里輸出還是 Damon- 指針接收者(func
(u *User)Notify() error)操作的是User 本身,所以其內(nèi)部的值會發(fā)生變化,這里輸出是 alimon
什么時候使用值接收者,什么時候使用指針接收者
https://stackoverflow.com/questions/27775376/value-receiver-vs-pointer-receiver-in-golang 防止文章被刪掉,下邊直接摘抄出來:
- If the receiver is a map, func or chan, don't use a pointer to it.
- If the receiver is a slice and the method doesn't reslice or reallocate the slice, don't use a pointer to it.
If the method needs to mutate(使 receiver 改變) the receiver, the receiver must be a pointer.- If the receiver is a struct that contains a sync.Mutex or similar synchronizing field, the receiver must be a pointer to avoid copying.
- If the receiver is a large struct or array, a pointer receiver is more efficient. How large is large? Assume it's equivalent to passing all its elements as arguments to the method. If that feels too large, it's also too large for the receiver.
Can function or methods, either concurrently or when called from this method, be mutating the receiver? A value type creates a copy of the receiver when the method is invoked, so outside updates will not be applied to this receiver. If changes must be visible in the original receiver, the receiver must be a pointer.- If the receiver is a struct, array or slice and any of its elements is a pointer to something that might be mutating, prefer a pointer receiver, as it will make the intention more clear to the reader.
If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used- If the receiver is a small array or struct that is naturally a value type (for instance, something like the time.Time type), with no mutable fields and no pointers, or is just a simple basic type such as int or string, a value receiver makes sense.
A value receiver can reduce the amount of garbage that can be generated; if a value is passed to a value method, an on-stack copy can be used instead of allocating on the heap. (The compiler tries to be smart about avoiding this allocation, but it can't always succeed.)Don't choose a value receiver type for this reason without profiling first.- Finally, when in doubt, use a pointer receiver.
總結(jié)一下:
- 如果需要改變 receiver 內(nèi)部的屬性值,選擇指針接收者;
- 如果 struct 中的一個方法使用了指針接收者,那么該 struct 內(nèi)的全部方法都是用指針接收者 - 一致性
1.3、使用接口
// sendNotification 接受一個實現(xiàn)了 notifier 接口的值并發(fā)送通知
func sendNotification(n notifier) {
n.notify()
}
func main() {
u := user{"nana"}
sendNotification(&u) // notify {nana}
}
二、實現(xiàn)多態(tài)

image.png
api.go
package api
// 定義接口
type Notifier interface {
Notify()
}
接口名和接口方法大寫表示 public,小寫表示 java 的 default(即包隔離級別)
user.go
package impl
import "fmt"
// 定義實現(xiàn)類 user
type User struct {
Name string
}
func (u *User) Notify() {
fmt.Println("user", *u)
}
admin.go
package impl
import "fmt"
// 定義實現(xiàn)類 Admin,首字母大寫表示 public
type Admin struct {
Name string
}
func (a *Admin) Notify() {
fmt.Println("admin", *a)
}
main.go
package main
import (
// 相對于 GOPATH/src 下的地址
"github.com/zhaojigang/helloworld/ooi/api"
"github.com/zhaojigang/helloworld/ooi/impl"
)
func sendNotification(n api.Notifier) {
n.Notify()
}
func main() {
u := impl.User{"nana"}
sendNotification(&u) // user {nana}
a := impl.Admin{"zhao"}
sendNotification(&a) // admin {zhao}
}
三、嵌入類型內(nèi)部接口實現(xiàn)提升
基于上述程序修改 admin.go 和 main.go。
admin.go
package impl
// 定義實現(xiàn)類 Admin,首字母大寫表示 public
type Admin struct {
User // 嵌入類型
Name string
}
注意:該類沒有實現(xiàn)接口 notifier 的 notify() 方法,但是由于該類的內(nèi)嵌類 User 實現(xiàn)了,內(nèi)嵌類會提升到外部類中,所以 Admin 類也是 notifier 接口的實現(xiàn)類,其實現(xiàn)函數(shù)就是 User#Notify() ,當然,可以 Admin 可以自己實現(xiàn) Notify() 來覆蓋 User#Notify()。
main.go
package main
import (
// 相對于 GOPATH/src 下的地址
"github.com/zhaojigang/helloworld/ooi/api"
"github.com/zhaojigang/helloworld/ooi/impl"
)
func sendNotification(n api.Notifier) {
n.Notify()
}
func main() {
u := impl.User{"nana"}
sendNotification(&u) // user {nana}
a := impl.Admin{u,"zhao"}
sendNotification(&a) // user {nana}
}
四、組合接口
=========================== Api1 ===========================
package api
type Api1 interface {
Api1()
}
=========================== Api2 ===========================
package api
type Api2 interface {
Api2()
}
=========================== Api12(組合接口) ===========================
package api
type Api12 interface {
Api1
Api2
}
=========================== Api12Impl ===========================
package impl
import "fmt"
type Api12Impl struct {
Name string
}
func (api12 *Api12Impl) Api1() {
fmt.Println("api1", api12.Name)
}
func (api12 *Api12Impl) Api2() {
fmt.Println("api2", api12.Name)
}
=========================== main ===========================
package main
import (
// 相對于 GOPATH/src 下的地址
"github.com/zhaojigang/helloworld/ooi/api"
"github.com/zhaojigang/helloworld/ooi/impl"
)
func sendNotification(n api.Api12) {
n.Api1() // api1 nana
n.Api2() // api2 nana
}
func main() {
api12 := impl.Api12Impl{"nana"}
sendNotification(&api12)
}