2013-11-23 10 views
5

私は次の種類があります。コール構造体BのスライスとのインタフェースAのスライスを受け入れるゴー機能(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で呼び出すことはできません。 QuoteStatementを実装しても、[]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) 

は今リプレイが、それはリプレイを使用するのですどのように簡単にその方法の外に行きたいライブラリの一部であると言います。これらのオブジェクトが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(conversation) 

、私の質問を: AがStatementインターフェイスを実装している限り、Replayに任意のタイプAのスライスを受け入れるための簡単な方法はありますか?

答えて

2

あなたの(長い)質問に対する非常に短い答えは:いいえ

私は空のインターフェースを取っConvertToStatmentとリプレイのソリューションが「素敵な」解決策であるとは思わない:私はfunc Replay([]Statement)を好みます呼び出し元はStatmentsのスライスを提供する必要があります。これははるかに明確であり、呼び出し側は自分のものを[]ステートメントに変換するか、[]ステートメントを直接構築することができます。

5

[]Quoteスライスのメモリ内レイアウトが[]Statementスライスと異なるため、これは不可能です。

[]Quoteスライスのバッキングアレイは、シーケンシャルQuote構造体で構成され、[]Statementスライスのバッキングアレイはインタフェース変数で構成されます。 Quote構造体(またはインタフェースを実装する他の型)を保持するだけでなく、インタフェース変数には、含まれている値の型情報へのポインタも格納されます。これは、Sayメソッド呼び出しをディスパッチする方法を決定するために必要です。

異なるデータレイアウトは、安全でないキャストでも、2つのスライスタイプを交換できないことを意味します。あるタイプがあり、他のタイプが必要な場合は、手動で変換する必要があります。

0

次のコードには、両方ともSay()関数を実装する2つの異なる構造型があります。あなたは両方のタイプを含む配列を作成し、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()) 
} 

Replay()

変更または Attributed{}について何も知りません。 スライスのタイプを導入する必要があります Quotes & Statements

関連する問題