Types 调用Go函数,该函数接受接口a的切片和结构B的切片(B实现a)

Types 调用Go函数,该函数接受接口a的切片和结构B的切片(B实现a),types,interface,go,Types,Interface,Go,我有以下几种: type Statement interface { Say() string } type Quote struct { quote string } func (p Quote) Say() string { return p.quote } func Replay(conversation []Statement) { for _, statement := range conversation { fmt.Printl

我有以下几种:

type Statement interface {
    Say() string
}

type Quote struct {
    quote string
}

func (p Quote) Say() string {
    return p.quote
}

func Replay(conversation []Statement) {
    for _, statement := range conversation {
        fmt.Println(statement.Say())
    }
}
我想我已经很好地理解了为什么接受类型为
[]语句
的参数的函数不能用
[]引号
调用;即使
Quote
实现了
语句
[]Quote
也没有实现
[]语句
<代码>[]语句甚至不是一个接口。它具有语句的类型
切片
。虽然Go隐式地从类型转换为接口类型,但它不会从类型
a
的片段隐式转换为接口
B
的片段

我们可以将引号显式转换为语句:

conversation := []Quote{
    Quote{"Nice Guy Eddie: C'mon, throw in a buck!"},
    Quote{"Mr. Pink: Uh-uh, I don't tip."},
    Quote{"Nice Guy Eddie: You don't tip?"},
    Quote{"Mr. Pink: Nah, I don't believe in it."},
    Quote{"Nice Guy Eddie: You don't believe in tipping?"},
}

// This doesn't work
// Replay(conversation)

// Create statements from quotes
statements := make([]Statement, len(conversation))
for i, quote := range conversation {
    statements[i] = quote
}

Replay(statements)
现在假设Replay是一个库的一部分,该库希望在使用Replay的易用性方面独辟蹊径。它允许您使用对象的任何片段调用Replay,只要这些对象实现了语句接口。为此,它采用以下转换方法:

func ConvertToStatements(its interface{}) ([]Statement, error) {
    itsValue := reflect.ValueOf(its)
    itsKind := itsValue.Kind()
    if itsKind != reflect.Array && itsKind != reflect.Slice {
        return nil, fmt.Errorf("Expected items to be an Array or a Slice, got %s", itsKind)
    }
    itsLength := itsValue.Len()
    items := make([]Statement, itsLength)
    for i := 0; i < itsLength; i++ {
        itsItem := itsValue.Index(i)
        if item, ok := itsItem.Interface().(Statement); ok {
            items[i] = item
        } else {
            return nil, fmt.Errorf("item #%d does not implement the Statement interface: %s", i, itsItem)
        }
    }
    return items, nil
}
我们现在可以直接使用引号调用Replay:

Replay(conversation)

最后,我的问题是:是否有一种更简单的方法允许Replay接受任何类型a的切片,只要a实现了语句接口?

对于您的(长)问题,非常简短的回答是:否


我不认为使用空接口的converttostatement和Replay解决方案是“好”的解决方案:我更喜欢
func Replay([]语句)
,调用方必须提供一部分声明。这更清楚,调用方可以将其内容转换为[]语句,也可以直接构造[]语句。

内存中的
[]引号
片段布局不同于
[]语句
片段,因此这是不可能的

[]Quote
切片的支持数组将由顺序
Quote
结构组成,而
[]语句
切片的支持数组由接口变量组成。除了保存
Quote
struct(或实现接口的任何其他类型)之外,接口变量还存储指向所包含值的类型信息的指针。这是确定如何分派
Say
方法调用所必需的


不同的数据布局意味着您无法交换这两种切片类型,即使是通过不安全的强制转换也不行:如果您有一种类型而需要另一种类型,则需要在它们之间手动转换。

以下代码有两种不同的结构类型,它们都实现了
Say()
函数。您可以创建一个包含这两种类型的数组,并调用
Replay()
,让它执行您想要的操作:

package main

import "fmt"

type Statement interface {
    Say() string
}
type Statements []Statement

type Quote struct {
    quote string
}
type Quotes []Quote

func (p Quote) Say() string {
    return p.quote
}

type Attributed struct {
    who   string
    quote string
}

func (p Attributed) Say() string {
    return p.who + ": " + p.quote
}


func Replay(conversation []Statement) {
    for _, s := range conversation {
        fmt.Println(s.Say())
    }
}

func (q Quotes) toStatements() Statements {
    conv := make(Statements, len(q))
    for i, v := range q {
        conv[i] = Statement(v)
    }
    return conv
}

func main() {
    conversation := Statements{
        Quote{"Nice Guy Eddie: C'mon, throw in a buck!"},
        Quote{"Mr. Pink: Uh-uh, I don't tip."},
        Attributed{"Nice Guy Eddie", "You don't tip?"},  // <= another type
        Quote{"Mr. Pink: Nah, I don't believe in it."},
        Quote{"Nice Guy Eddie: You don't believe in tipping?"},
    }

    myquotes := Quotes{
        Quote{"Nice Guy Eddie: C'mon, throw in a buck!"},
        Quote{"Mr. Pink: Uh-uh, I don't tip."},
        Quote{"Nice Guy Eddie: You don't tip?"},
        Quote{"Mr. Pink: Nah, I don't believe in it."},
        Quote{"Nice Guy Eddie: You don't believe in tipping?"},
    }

    Replay(conversation)
    Replay(myquotes.toStatements())
}
主程序包
输入“fmt”
类型语句接口{
Say()字符串
}
类型语句[]语句
类型Quote结构{
引号字符串
}
键入Quotes[]引号
func(p引号)Say()字符串{
返回p.quote
}
类型属性结构{
谁串的
引号字符串
}
func(p属性化)Say()字符串{
返回p.who+“:”+p.quote
}
func重播(会话[]语句){
对于uS:=范围对话{
fmt.Println(s.Say())
}
}
func(q引号)to语句()语句{
conv:=make(语句,len(q))
对于i,v:=范围q{
conv[i]=语句(v)
}
返回转换
}
func main(){
对话:=陈述{
引用{“好人埃迪:来吧,投入一块钱!”,
引用{“平克先生:嗯,我不给小费。”},
归因于{“好人埃迪”,“你不给小费吗?”}//
package main

import "fmt"

type Statement interface {
    Say() string
}
type Statements []Statement

type Quote struct {
    quote string
}
type Quotes []Quote

func (p Quote) Say() string {
    return p.quote
}

type Attributed struct {
    who   string
    quote string
}

func (p Attributed) Say() string {
    return p.who + ": " + p.quote
}


func Replay(conversation []Statement) {
    for _, s := range conversation {
        fmt.Println(s.Say())
    }
}

func (q Quotes) toStatements() Statements {
    conv := make(Statements, len(q))
    for i, v := range q {
        conv[i] = Statement(v)
    }
    return conv
}

func main() {
    conversation := Statements{
        Quote{"Nice Guy Eddie: C'mon, throw in a buck!"},
        Quote{"Mr. Pink: Uh-uh, I don't tip."},
        Attributed{"Nice Guy Eddie", "You don't tip?"},  // <= another type
        Quote{"Mr. Pink: Nah, I don't believe in it."},
        Quote{"Nice Guy Eddie: You don't believe in tipping?"},
    }

    myquotes := Quotes{
        Quote{"Nice Guy Eddie: C'mon, throw in a buck!"},
        Quote{"Mr. Pink: Uh-uh, I don't tip."},
        Quote{"Nice Guy Eddie: You don't tip?"},
        Quote{"Mr. Pink: Nah, I don't believe in it."},
        Quote{"Nice Guy Eddie: You don't believe in tipping?"},
    }

    Replay(conversation)
    Replay(myquotes.toStatements())
}