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

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

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]\n", msg, s.topic)
  return nil
}
func main() {
  var producer Producer
  kafkaProducer := KafkaProducer{"like_topic", "kafkaProducer"}
  producer = kafkaProducer
  fmt.Printf("%T, %#v \n", 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 \n", 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]\n", 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\n", producer, producer)
  producer = &rocketProducer
  fmt.Printf("%T, %#v \n", 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\n", kafkaProducer1.name)
  kafkaProducer2 := &KafkaProducer{"like_topic", "kafkaProducer"}
  kafkaProducer2.updateName("bbb")
  fmt.Printf("kafkaProducer2.name = %v\n", kafkaProducer2.name)
  rocketProducer1 := RocketProducer{"collection_topic","rocketProducer"}
  rocketProducer1.updateName("aaa")
  fmt.Printf("rocketProducer1.name = %v\n", rocketProducer1.name)
  rocketProducer2 := &RocketProducer{"collection_topic","rocketProducer"}
  rocketProducer2.updateName("bbb")
  fmt.Printf("rocketProducer2.name = %v\n", rocketProducer2.name)
}

输出

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

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

相关文章
|
7月前
|
编译器 Go 开发者
Golang 语言怎么使用接口编程?
Golang 语言怎么使用接口编程?
44 0
|
7月前
|
安全 Go PHP
Golang 语言的编程技巧之变量
Golang 语言的编程技巧之变量
14 0
|
7月前
|
JSON 缓存 Go
Golang 语言 Web 框架 beego v2 之控制器方法和输入输出数据
Golang 语言 Web 框架 beego v2 之控制器方法和输入输出数据
56 0
|
1天前
|
JSON Go 数据格式
golang学习7,glang的web的restful接口结构体传参
golang学习7,glang的web的restful接口结构体传参
|
1天前
|
JSON Go 数据格式
golang学习6,glang的web的restful接口传参
golang学习6,glang的web的restful接口传参
|
1天前
|
JSON Go 数据格式
golang学习5,glang的web的restful接口
golang学习5,glang的web的restful接口
|
1天前
|
Go
golang学习4,glang的web接口
golang学习4,glang的web接口
|
1天前
|
安全 Go
Golang深入浅出之-接口(Interfaces)详解:抽象、实现与空接口
【4月更文挑战第22天】Go语言接口提供抽象能力,允许类型在不暴露实现细节的情况下遵循行为约定。接口定义了一组方法签名,类型实现这些方法即实现接口,无需显式声明。接口实现是隐式的,通过确保类型具有接口所需方法来实现。空接口`interface{}`接受所有类型,常用于处理任意类型值。然而,滥用空接口可能丧失类型安全性。理解接口、隐式实现和空接口的使用能帮助编写更健壮的代码。正确使用避免方法,如确保方法签名匹配、检查接口实现和谨慎处理空接口,是关键。
22 1
|
1天前
|
Go 开发者
Golang深入浅出之-Go语言方法与接收者:面向对象编程初探
【4月更文挑战第22天】Go语言无类和继承,但通过方法与接收者实现OOP。方法是带有接收者的特殊函数,接收者决定方法可作用于哪些类型。值接收者不会改变原始值,指针接收者则会。每个类型有相关方法集,满足接口所有方法即实现该接口。理解并正确使用这些概念能避免常见问题,写出高效代码。Go的OOP机制虽不同于传统,但具有灵活性和实用性。
23 1
|
1天前
|
Go
Golang深入浅出之-Go语言基础语法:变量声明与赋值
【4月更文挑战第20天】本文介绍了Go语言中变量声明与赋值的基础知识,包括使用`var`关键字和简短声明`:=`的方式,以及多变量声明与赋值。强调了变量作用域、遮蔽、初始化与零值的重要性,并提醒读者注意类型推断时的一致性。了解这些概念有助于避免常见错误,提高编程技能和面试表现。
27 0