web-dev-qa-db-ja.com

Golangでの匿名インターフェースの実装

Goでは、匿名でインターフェースを満たす方法はありますか?あるようには見えませんが、これは私の最善の試みでした。

プレイグラウンド

package main

import "fmt"

type Thing interface {
    Item() float64
    SetItem(float64)
}

func newThing() Thing {
    item := 0.0
    return struct {
        Item (func() float64)
        SetItem (func(float64))
    }{
        Item: func() float64 { return item },
        SetItem: func(x float64) { item = x },
    }
}

func main() {
    thing := newThing()
    fmt.Println("Hello, playground")
    fmt.Println(thing)
}
38
jocull

Goは method sets を使用して、どのメソッドが型に属するかを宣言します。レシーバータイプ(メソッド)で関数を宣言する方法は1つしかありません。

func (v T) methodName(...) ... { }

ネストされた関数は禁止されているため、匿名の構造体に設定されたメソッドを定義する方法はありません。

これを許可しない2番目のことは、メソッドが読み取り専用であることです。 メソッド値 は、メソッドを渡してゴルーチンで使用できるようにするために導入されましたが、メソッドセットを操作することはできません。

代わりにできることは、ProtoThingを提供し、匿名構造体の基礎となる実装を参照することです( on play ):

type ProtoThing struct { 
    itemMethod func() float64
    setItemMethod func(float64)
}

func (t ProtoThing) Item() float64 { return t.itemMethod() }
func (t ProtoThing) SetItem(x float64) { t.setItemMethod(x) }

// ...

t := struct { ProtoThing }{}

t.itemMethod = func() float64 { return 2.0 }
t.setItemMethod = func(x float64) { item = x }

これは、ProtoThingを埋め込むことでメソッドセットが継承されるため機能します。したがって、匿名構造体はThingインターフェイスも満たします。

45
nemo

以下は、匿名関数を使用してインターフェイスを満たすための適切な方法です。

type Thinger interface {
    DoThing()
}

type DoThingWith func()

// Satisfy Thinger interface.
// So we can now pass an anonymous function using DoThingWith, 
// which implements Thinger.
func (thing DoThingWith) DoThing() {
    // delegate to the anonymous function
    thing()
}

type App struct {
}

func (a App) DoThing(f Thinger) {
    f.DoThing()
}


//...Somewhere else in your code:
app := App{}

// Here we use an anonymous function which satisfies the interface
// The trick here is to convert the anonymous function to the DoThingWith type
// which delegates to the anonymous function

app.DoThing(DoThingWith(func() {
    fmt.Println("Hey interface, are you satisfied?")
}))

遊び場: https://play.golang.org/p/k8_X9g2NYc

nb、httpパッケージのHandlerFuncは次のパターンを使用しているように見えます: https://golang.org/pkg/net/http/#HandlerFunc

edit:明確にするために、タイプDoThingをDoThingWithに変更しました。更新された遊び場

11
Allen Hamilton

メソッドを使用して構造体をインスタンス化することはできません。関数として宣言する必要がありますが、Goでは関数は「ファーストクラスの市民」であるため、JavaScriptのようにフィールド値になります(ただし型付けされます)。

インターフェイスを実装するfuncフィールドを受け入れる汎用構造体を作成できます。

package main

import "fmt"

type Thing interface {
    Item() float64
    SetItem(float64)
}

// Implements Thing interface
type thingImpl struct {
    item    func() float64
    setItem func(float64)
}
func (i thingImpl) Item() float64     { return i.item() }
func (i thingImpl) SetItem(v float64) { i.setItem(v) }

func newThing() Thing {
    item := 0.0
    return thingImpl{
        item:    func() float64 { return item },
        setItem: func(x float64) { item = x },
    }
}

func main() {
    thing := newThing()
    fmt.Println("Hello, playground")
    fmt.Println(thing)
}
1
tothemario