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

我有以下类型:


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())

    }

}

我想我很清楚为什么[]Statement不能用[]Quote;调用接受类型参数的函数;即使Quote执行Statement,[]Quote也没有执行[]Statement。[]Statement甚至不是一个接口。它有类型slice of Statement. 虽然 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。只要这些对象实现 Statement 接口,它就允许您使用任何对象切片调用 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

}

回放看起来像这样:


func Replay(its interface{}) {

    conversation := ConvertToStatements(its)

    for _, statement := range conversation {

        fmt.Println(statement.Say())

    }

}

我们现在可以直接用引号调用 Replay:


Replay(conversation)

最后,我的问题是:有没有更简单的方法可以让 Replay 接受任何类型 A 的切片,只要 A 实现 Statement 接口?


有只小跳蛙
浏览 232回答 3
3回答

慕斯王

[]Quote切片的内存布局与切片不同[]Statement,因此这是不可能的。所述的背衬阵列[]Quote片将包括连续的Quote结构,而[]Statement片的背衬阵列由接口变量。除了保存Quote结构体(或任何其他实现接口的类型),接口变量还存储指向所包含值的类型信息的指针。这需要确定如何分派Say方法调用。不同的数据布局意味着您不能交换这两种切片类型,即使通过不安全的强制转换也是如此:如果您有一种类型而需要另一种类型,则需要在它们之间手动转换。

莫回无

对您的(长)问题的简短回答是:不。我不认为你的 ConvertToStatment 和 Replay 采用空接口的解决方案是一个“不错”的解决方案:我更喜欢func Replay([]Statement)调用者必须提供一部分 Statments。这更清楚,调用者可以将他们的内容转换为 []Statement 或直接构造一个 []Statement。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go