Golang中为什么无法从main包调用包含方法集合的接口?
Golang中为什么无法从main包调用包含方法集合的接口? 我对Go语言还非常陌生,正在尝试理解封装在Go中是如何实际工作的。
我有以下结构:
-- package a
-a_core.go
-a.go
-models.go
-- main.go
在models.go中,我有用于API调用的请求和响应的结构体。
a.go中有一个私有的空结构体和一个我希望公开的、包含各种方法的公共接口。 a_core.go只包含一些业务逻辑,这些逻辑将在我的接口实现中被调用。 然后我有一个main.go,在那里我直接调用公共接口。
package a
type myFunction struct{}
type MyFunc interface {
Create(myData *MyData) (*MyData, error)
Fetch(test string)
Delete(test string)
}
//可以公开访问的具体实现
func (a *myFunction) Create(data *MyData) (*MyData, error) {
return nil, nil
}
func (a *myFunction) Fetch(test string) {
}
func (a *myFunction) Delete(test string) {
}
在main.go中,我首先创建带有值的MyData指针来调用接口
data := &a.MyData{
/////
}
result, err := a.MyFunc.Create(data)
当我这样做时,我得到以下错误:
调用a.MyFunc.Create的参数太少
无法将data(类型为*a.MyData的变量)用作a.MyFunc值传递给a.MyFunc.Create:缺少方法CreatecompilerInvalidIfaceAssign
请问我哪里做错了?
更多关于Golang中为什么无法从main包调用包含方法集合的接口?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
大家好, 这些回复很有帮助。那个例子只是一个简单的示例,而我正在构建一个库,所以,基于我在C#和Java方面的背景,我需要通过接口来公开函数。
更多关于Golang中为什么无法从main包调用包含方法集合的接口?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我并非有意要回答这个问题;我在上一篇文章中已经说过,你的答案是正确的 🙂。我的观点是,除了展示如何在 Go 中使用接口之外,在这种情况下,在这里使用接口没有任何意义。你可以直接使用 MyFunction(或你上一篇文章中的 MyStruct):
myStruct := MyStruct{}
myResult := myStruct.MyPointerReceiverMethod()
你的答案是正确的,因为它展示了如何将具体类型转换为接口并在该接口上调用方法。我怀疑 Gbubemi(或其他可能看到这篇论坛帖子的读者)的下一个问题可能是:为什么首先要使用接口,这正是我想在这里评论的。
肖恩
感谢你的意见,但除非我误解了什么,否则我认为你的日志记录器示例并没有解决这个问题。
最初的问题是如何调用一个拥有指针接收器的方法。
我能找到的最佳资料展示了分两步走的方法:先初始化结构体,然后用该结构体的地址来初始化接口。
通过这个接口,你就可以调用那个拥有指针接收器的方法了。
这并不完全优雅,但确实有效。如果有一个更简洁的方法,我很乐意看到,但目前,这就是我能想到的最好方案:
myStruct := MyStruct{}
var myInterface MyInterface = &myStruct
myResult := myInterface.MyPointerReceiverMethod())
是的——我也是个新手,前几天也遇到了这个问题。
第一个问题是您将 myFunction 设为了私有。
第二个问题是,要访问带有指针接收器的接口方法,您必须:
- 创建结构体的具体版本。
- 将地址转换为接口。
我想您会发现以下代码可以工作:
package main
import "fmt"
type MyFunction struct{}
type MyInterface interface {
Create(myData *MyData) (*MyData, error)
Fetch(test string)
Delete(test string)
}
type MyData struct {
S string
}
func (a *MyFunction) Create(data *MyData) (*MyData, error) {
return nil, nil
}
func (a *MyFunction) Fetch(test string) {
}
func (a *MyFunction) Delete(test string) {
}
func main() {
myFunc := MyFunction{}
var myInt MyInterface = &myFunc
md, _ := myInt.Create(&MyData{})
fmt.Println(md)
}
正如我所说,我是个新手,所以通常的警告也适用。如果我说的不对,希望有专家能指正。
我已经使用Go语言几年了,但我觉得我还不算任何方面的专家!
@scotpip 的回答是正确的:它向你展示了如何将一个具体的事物(即 MyFunction)"转换"成一个接口 MyInterface,然后调用其上的方法。
话虽如此,在这种情况下使用接口是有些牵强的。根据你的问题,我怀疑这只是为了初步涉足接口并了解其工作原理的一次学习体验,我无意贬低这一点;欢迎随时提出更多问题!我对scotpip的回答或你的问题都没有意见,但我想履行我作为Gopher的"职责",明确说明在这种情况下,你可以直接使用你的 MyFunction,而不是先将其转换为接口。
当你并不关心你所使用的事物的确切类型,而只关心你能用它做什么时,接口是一个极好的工具。
例如,假设你希望程序输出日志消息用于调试。虽然已经有一个 log 包可以为你做这件事,但假设它不存在,而你想做一些简单的事情。你可以这样做:
// logger 持有日志消息将被写入的写入器。
var logger io.Writer = os.Stdout // 默认情况下,写入标准输出
// SetLogger 允许将程序日志输出更改为另一个目标。
func SetLogger(w io.Writer) {
logger = w
}
// 将应用程序的消息记录到日志记录器
func log(message string, args ...interface{}) {
fmt.Fprintf(logger, message, args...)
}
func main() {
log("starting application...")
num, den := 3, 0
res := div(num, den)
fmt.Println("%d ÷ %d = %d", num, den, res)
log("exiting application...")
}
func div(num, den int) int {
if den == 0 {
log("attempt to divide %d by 0", num)
return 0 // TODO: 返回错误而不是仅仅返回0
}
return num / den
}
那么,如果你不想写入控制台,而是想写入文件呢?你只需更改日志记录器:
func main() {
// ...
f, err := os.Create("log.txt")
if err != nil {
panic(err) // TODO: 在这里更好地处理错误。
}
defer f.Close()
SetLogger(f)
// ...
}
如果你的程序不关心日志消息被写入哪里,只要无论是什么,它都有一个 Write 方法,那么接口就是一个完美的选择。
你的代码有几个关键问题需要修正:
- 接口不能直接调用方法 - 接口是类型定义,不是可实例化的对象
- 需要实现接口的具体类型实例
- 接口变量需要被赋值具体的实现
以下是修正后的代码:
修正后的代码结构
a/models.go
package a
type MyData struct {
// 你的字段定义
ID string
Name string
}
a/a.go
package a
// 私有结构体
type myFunction struct{}
// 公共接口
type MyFunc interface {
Create(myData *MyData) (*MyData, error)
Fetch(test string)
Delete(test string)
}
// 工厂函数,返回接口类型
func NewMyFunc() MyFunc {
return &myFunction{}
}
// 接口实现
func (m *myFunction) Create(data *MyData) (*MyData, error) {
// 实现逻辑
return data, nil
}
func (m *myFunction) Fetch(test string) {
// 实现逻辑
}
func (m *myFunction) Delete(test string) {
// 实现逻辑
}
main.go
package main
import (
"fmt"
"your_module_path/a" // 替换为你的实际模块路径
)
func main() {
// 1. 创建接口实例
myFunc := a.NewMyFunc()
// 2. 创建数据
data := &a.MyData{
ID: "123",
Name: "Test",
}
// 3. 通过接口调用方法
result, err := myFunc.Create(data)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Result: %+v\n", result)
}
替代方案:直接使用结构体
如果你不需要接口的灵活性,也可以直接导出结构体:
a/a.go(直接结构体版本)
package a
// 公共结构体
type MyFunction struct{}
func (m *MyFunction) Create(data *MyData) (*MyData, error) {
return data, nil
}
func (m *MyFunction) Fetch(test string) {
// 实现逻辑
}
func (m *MyFunction) Delete(test string) {
// 实现逻辑
}
main.go(直接结构体版本)
package main
import (
"fmt"
"your_module_path/a"
)
func main() {
// 直接实例化结构体
myFunc := &a.MyFunction{}
data := &a.MyData{
ID: "123",
Name: "Test",
}
result, err := myFunc.Create(data)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Result: %+v\n", result)
}
关键点说明
- 接口是类型,不是值:
a.MyFunc是一个接口类型,不能直接调用方法 - 需要具体实现:必须有一个实现了接口所有方法的具体类型
- 接口变量赋值:接口变量必须被赋值为具体实现的实例
- 工厂函数模式:使用
NewMyFunc()这样的工厂函数是常见的做法,它隐藏了具体实现类型
错误信息 “调用a.MyFunc.Create的参数太少” 是因为你试图在接口类型上直接调用方法,这在Go中是不允许的。必须先创建实现了该接口的具体类型的实例,然后通过该实例调用方法。

