zl程序教程

您现在的位置是:首页 >  其他

当前栏目

golang 接口变量的赋值和方法的调用

2023-03-14 22:46:15 时间

1、「方法」概念介绍

带有接收者的函数称为方法,方法是 go 语言中一种替代面向对象的方式。函数内部可以使用接收者,使用完之后根据接收者的类型是值类型还是指针类型选择是否自动覆盖原接收者。

1456655-20220716175455351-455636636.png

可以随意安排函数定义的顺序,编译器会在执行前扫描每个文件。

2、接口变量的赋值

接口定义为一个方法的集合。方法包含实际的代码。换句话说,一个接口就是定义, 而方法就是实现。因此,接收者不能定义为接口类型,这样做的话会引起 invalid receiver type ... 的编译器错误。

接收者类型必须是 T 或 *T,这里的 T 是类型名。T 叫做接收者基础类型或 简称基础类型。

如果接收者是实现了接口定义申明的所有方法,那么称这个接收者实现了这个接口。

2.1 方法的接收者是值类型

当方法的接收者是值类型时,接口变量可以被赋值为指针类型或者值类型的接收者对象

package main
import (
    "fmt"
)
type Producer interface {
    send(string) error
}
type KafkaProducer struct {
    topic string
    name  string
}
// 接收者是值类型
func (s KafkaProducer) send(msg string) error {
    fmt.Printf("send %s to topic [%s]
", msg, s.topic)
    return nil
}
func main() {
    var producer Producer
    kafkaProducer := KafkaProducer{"like_topic", "kafkaProducer"}
    producer = kafkaProducer
    fmt.Printf("%T, %#v 
", producer, producer) // main.KafkaProducer, main.KafkaProducer{topic:"like_topic", name:"kafkaProducer"}
    producer.send("hahha") // send hahha to topic [like_topic]
    producer = &kafkaProducer
    fmt.Printf("%T, %#v 
", producer, producer) // *main.KafkaProducer, &main.KafkaProducer{topic:"like_topic", name:"kafkaProducer"}
    producer.send("hahha") // send hahha to topic [like_topic]
}

2.2 方法的接收者是指针(引用)类型

当接收者是指针类型时,接口变量只能被赋值为指针类型的接收者对象,如果被赋值为了值类型的接收者对象,会有类似下面程序的报错Cannot use 'rocketProducer' (type RocketProducer) as the type Producer Type does not implement 'Producer' as the 'send' method has a pointer receiver

package main
import (
    "fmt"
)
type Producer interface {
    send(string) error
}
type RocketProducer struct {
    topic string
    name  string
}
// 接收者是指针类型
func (s *RocketProducer) send(msg string) error {
    fmt.Printf("send %s to topic [%s]
", msg, s.topic)
    return nil
}
func main() {
    var producer Producer
    rocketProducer := RocketProducer{"collection_topic","rocketProducer"}
    producer = rocketProducer // Cannot use 'rocketProducer' (type RocketProducer) as the type Producer Type does not implement 'Producer' as the 'send' method has a pointer receiver
    fmt.Sprintf("%T, %#v
", producer, producer)
    producer = &rocketProducer
    fmt.Printf("%T, %#v 
", producer, producer) // *main.RocketProducer, &main.RocketProducer{topic:"collection_topic", name:"rocketProducer"}
    producer.send("hahha") // send hahha to topic [collection_topic]
}

2.3 对象没有实现接口

如果对象没有实现接口的方法,那么不能被赋值给接口变量,否则会有类似如下程序的报错Cannot use 'rabbitProducer' (type RabbitProducer) as the type Producer Type does not implement 'Producer' as some methods are missing: send(string) error

package main
import (
    "fmt"
)
type Producer interface {
    send(string) error
}
type RabbitProducer struct {
    topic string
    name  string
}
func main() {
    var producer Producer
    rabbitProducer := RabbitProducer{"share_topic", "rabbitProducer"}
    producer = rabbitProducer // Cannot use 'rabbitProducer' (type RabbitProducer) as the type Producer Type does not implement 'Producer' as some methods are missing: send(string) error
}

3、方法调用

接收者是指针则在方法修改对象的属性会影响原来的对象,如果接收者是对象,那么在方法中修改对象的属性不会影响原来的对象。跟上面的接口变量赋值略有不同,不管方法里的接收者是指针类型还是对象类型,都可以同时使用指针对象和值对象调用。golang 通过语法糖,使得值对象也能直接调用接收者为指针类型的方法。

package main
import "fmt"
type KafkaProducer struct {
    topic string
    name  string
}
// 接收者是值类型
func (s KafkaProducer) updateName(newName string) error {
    s.name = newName
    return nil
}
type RocketProducer struct {
    topic string
    name  string
}
// 接收者是指针类型
func (s *RocketProducer) updateName(newName string) error {
    s.name = newName
    return nil
}
func main() {
    kafkaProducer1 := KafkaProducer{"like_topic", "kafkaProducer"}
    kafkaProducer1.updateName("aaa")
    fmt.Printf("kafkaProducer1.name = %v
", kafkaProducer1.name)
    kafkaProducer2 := &KafkaProducer{"like_topic", "kafkaProducer"}
    kafkaProducer2.updateName("bbb")
    fmt.Printf("kafkaProducer2.name = %v
", kafkaProducer2.name)
    rocketProducer1 := RocketProducer{"collection_topic","rocketProducer"}
    rocketProducer1.updateName("aaa")
    fmt.Printf("rocketProducer1.name = %v
", rocketProducer1.name)
    rocketProducer2 := &RocketProducer{"collection_topic","rocketProducer"}
    rocketProducer2.updateName("bbb")
    fmt.Printf("rocketProducer2.name = %v
", rocketProducer2.name)
}

输出

kafkaProducer1.name = kafkaProducer
kafkaProducer2.name = kafkaProducer
rocketProducer1.name = aaa
rocketProducer2.name = bbb

参考golang杂记01-golang接口的赋值问题GO接口赋值与方法接收者问题