インターフェイスの意味は何ですか{}?


133

私はインターフェイスが初めてで、githubで SOAPリクエストを実行しようとしています

意味がわかりません

Msg interface{}

このコードでは:

type Envelope struct {
    Body `xml:"soap:"`
}

type Body struct {
    Msg interface{}
}

私は同じ構文を観察しました

fmt.Println

しかし、何が達成されているのか理解していない

interface{}

20
interface{}それは多かれ少なかれvoid *Cと同等です。それは何かを指すことができ、それを使用するにはキャスト/タイプアサーションが必要です。
Nick Craig-Wood

インターフェイスの意味は何ですか{}?stackoverflow.com/a/62337836/12817546を参照してください。
トムJ

回答:


189

記事「Goでのインターフェースの使用方法」(「Russ Coxのインターフェースの説明」に基づく)を参照できます

インターフェースと

インターフェースは次の2つです。

  • メソッドのセットであり、
  • それもタイプです

interface{}タイプは、空のインターフェイスはないメソッドを持たないインタフェースです。

implementsキーワードはないため、すべての型は少なくともゼロのメソッドを実装し、インターフェイスを満たすことは自動的に行われるため、すべての型が空のインターフェイスを満たします
つまりinterface{}、パラメータとして値を受け取る関数を作成する場合、その関数に任意の値を指定できます

(それがMsgあなたの質問で何を表しているかです:任意の値)

func DoSomething(v interface{}) {
   // ...
}

ここが混乱するところです:

DoSomething関数の中で、型はvですか?

初心者のホリネズミは「vあらゆる種類のものである」と信じさせられますが、それは間違っています。
vタイプはありません。それはであるinterface{}タイプ

DoSomething関数に値を渡すと、Goランタイムは型変換を実行し(必要な場合)、値を値に変換しinterface{}ます
すべての値は実行時に型を1つだけ持ち、その1 vつの静的型はinterface{}です。

インターフェース値は2ワードのデータで構成されます

  • 1つの単語は、値の基になる型のメソッドテーブルを指すために使用されます。
  • もう1つの単語は、その値によって保持されている実際のデータを指すために使用されます。

補遺:これは、インターフェース構造に関してRussの記事が非常に完全なものでした:

type Stringer interface {
    String() string
}

インターフェイスの値は、インターフェイスに格納されているタイプに関する情報へのポインタと関連データへのポインタを提供する2ワードのペアとして表されます。
タイプStringerのインターフェース値にbを割り当てると、インターフェース値の両方のワードが設定されます。

http://research.swtch.com/gointer2.png

インターフェイス値の最初の単語は、私がインターフェイステーブルまたはitableと呼ぶものを指しています(発音はi-table。ランタイムソースでは、C実装名はItabです)。
itableは、関係する型に関するメタデータで始まり、関数ポインタのリストになります。
itableは動的タイプではなくインターフェースタイプに対応することに注意してください
この例に関しては、のためのITable Stringer種類のバイナリを保持しているだけで満足ストリンガーに使用される方法一覧ですString:バイナリの他のメソッドを(Get)には外観をしませんitable

インターフェイス値の2番目の単語は、実際のデータ、この場合はのコピーを指していますb
割り当てはvar s Stringer = bコピー可能bではなく、ポイントをb同じ理由でvar c uint64 = bあれば:コピーを作成しb、後で変更、sおよびをc元の値ではなく、新しいものを持っていることになっています。
インターフェイスに格納される値は任意に大きくなる可能性がありますが、インターフェイス構造で値を保持するために使用されるのは1ワードだけなので、割り当てはヒープにメモリのチャンクを割り当て、1ワードスロットにポインタを記録します。


4
「2ワードのデータ」とはどういう意味ですか?具体的には、「単語」とはどういう意味ですか?
Mingyu 2014

3
@Mingyu私はそれらの2つの単語(32ビットポイント)を説明するために回答を完了しました。
VonC 2014

2
@Mingyu:VonCは、コンピューターアーキテクチャの意味での単語を指します。固定サイズのデータ​​を定義するビットのコレクションです。ワードサイズは、使用しているプロセッサアーキテクチャによって決まります。
Dan Esparza 2016年

1
返信ありがとう@VonC ...本当は私が物事を尋ねるときにダウンポストになるのにうんざりしているということです...ほとんどの場合、人々は私がドキュメントを読むべきだと言っています...私が感じたらあなたの提案を思い出しますそのための投稿を適切に書くつもりです...しかし、私は実際に尋ねる別の方法を考えることはできません。とにかく感謝し、私の意志を失いました。これをご覧ください:stackoverflow.com/questions/45577301/…私が尋ねたくない理由を明確にするために。
ビクター

1
@vic問題ありません。そして、あなたの以前の悪い経験をアスカーとして申し訳ありません。コメントが質疑応答に適さないだけです。
VonC 2017

34

interface{}は、独自のカスタムタイプを含め、任意のタイプの値を配置できることを意味します。Goのすべての型は、空のインターフェースを満たします(interface{}空のインターフェースです)。
あなたの例では、メッセージフィールドは任意のタイプの値を持つことができます。

例:

package main

import (
    "fmt"
)

type Body struct {
    Msg interface{}
}

func main() {
    b := Body{}
    b.Msg = "5"
    fmt.Printf("%#v %T \n", b.Msg, b.Msg) // Output: "5" string
    b.Msg = 5

    fmt.Printf("%#v %T", b.Msg, b.Msg) //Output:  5 int
}

遊び場に行く


12

これは空のインターフェースと呼ばれ、すべてのタイプによって実装されMsgます。つまり、フィールドに何でも置くことができます。

例:

body := Body{3}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:3}

body = Body{"anything"}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:"anything"}

body = Body{body}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:main.Body{Msg:"anything"}}

これは、型がインターフェイスのすべてのメソッドを持つとすぐに型がインターフェイスを実装するという事実の論理的な拡張です。


これは、ユーザー定義の構造体へのintである可能性があることを意味しますか?
ユーザー

11

ここにはすでに良い答えがあります。それを直感的に理解したい人のために私も追加しましょう:


インターフェース

1つのメソッドを持つインターフェースは次のとおりです。

type Runner interface {
    Run()
}

したがって、Run()メソッドを持つすべての型は、Runnerインターフェースを満たします。

type Program struct {
    /* fields */
}

func (p Program) Run() {
    /* running */
}

func (p Program) Stop() {
    /* stopping */
}
  • ProgramタイプにはStopメソッドもありますが、必要なのはインターフェイスのすべてのメソッドを備えてそれを満たすことだけなので、Runnerインターフェイスも満たします。

  • そのため、Runメソッドがあり、Runnerインターフェースを満たしています。


空のインターフェース

メソッドのない名前付きの空のインターフェースは次のとおりです。

type Empty interface {
    /* it has no methods */
}

したがって、すべてのタイプがこのインターフェースを満たします。なぜなら、このインターフェースを満たすためのメソッドは必要ないからです。例えば:

// Because, Empty interface has no methods, following types satisfy the Empty interface
var a Empty

a = 5
a = 6.5
a = "hello"

しかし、上記のプログラムタイプはそれを満たしていますか?はい:

a = Program{} // ok

interface {}は、上記の空のインターフェースと同じです。

var b interface{}

// true: a == b

b = a
b = 9
b = "bye"

ご覧のとおり、不思議なことは何もありませんが、悪用は非常に簡単です。できるだけ遠ざけてください。


https://play.golang.org/p/A-vwTddWJ7G


type Runner interface囲碁遊び場の例では未使用です。
トムJ

9

Golang仕様から:

インターフェイスタイプは、そのインターフェイスと呼ばれるメソッドセットを指定します。インターフェイス型の変数は、インターフェイスのスーパーセットであるメソッドセットを持つ任意の型の値を格納できます。そのようなタイプは、インターフェースを実装すると言われています。インターフェイス型の初期化されていない変数の値はnilです。

型は、そのメソッドのサブセットを含むインターフェースを実装するため、いくつかの異なるインターフェースを実装できます。たとえば、すべてのタイプは空のインターフェースを実装しています。

インターフェース{}

把握する概念は次のとおりです。

  1. すべてにタイプがあります。あなたが今度はT.ましょうと言う、それを呼びましょう、新しい型を定義することができます私たちのタイプはT3つの方法があります:ABC
  2. タイプに指定されたメソッドのセットは、「インターフェースタイプ」と呼ばれます。この例では、T_interfaceと呼びます。等しいT_interface = (A, B, C)
  3. メソッドのシグネチャを定義することにより、「インターフェースタイプ」を作成できます。MyInterface = (A, )
  4. タイプ「インターフェースタイプ」の変数を指定すると、インターフェースのスーパーセットであるインターフェースを持つタイプのみを変数に割り当てることができます。つまり、含まれるすべてのメソッドは内部に含まれる必要がありますMyInterfaceT_interface

すべてのタイプのすべての「インターフェースタイプ」が空のインターフェースのスーパーセットであると推測できます。


1

@VonCによる優れた回答と@ NickCraig-Woodによるコメントを拡張する例。interface{}何かを指すことができ、それを使用するにはキャスト/タイプのアサーションが必要です。

package main

import (
    . "fmt"
    "strconv"
)

var c = cat("Fish")
var d = dog("Bone")

func main() {
    var i interface{} = c
    switch i.(type) {
    case cat:
        c.Eat() // Fish
    }

    i = d
    switch i.(type) {
    case dog:
        d.Eat() // Bone
    }

    i = "4.3"
    Printf("%T %v\n", i, i) // string 4.3
    s, _ := i.(string)      // type assertion
    f, _ := strconv.ParseFloat(s, 64)
    n := int(f)             // type conversion
    Printf("%T %v\n", n, n) // int 4
}

type cat string
type dog string
func (c cat) Eat() { Println(c) }
func (d dog) Eat() { Println(d) }

iは値を持つ空のインターフェースの変数ですcat("Fish")。インターフェイスタイプの値からメソッド値を作成することは正当です。https://golang.org/ref/spec#Interface_typesを参照してください

タイプスイッチiは、インターフェイスタイプがであることを確認しますcat("Fish")https://golang.org/doc/effective_go.html#type_switchを参照してくださいi次にに再度割り当てられdog("Bone")ます。タイプスイッチは、iインターフェイスのタイプがに変更されたことを確認しますdog("Bone")

代入を試行して、型Tがインターフェイスを実装していることを確認するようコンパイラーに要求することもできます。https://golang.org/doc/faq#guarantee_satisfies_interfaceおよびhttps://stackoverflow.com/a/60663003/12817546を参照してくださいIvar _ I = T{}

すべてのタイプが空のインターフェースを実装しますinterface{}https://talks.golang.org/2012/goforc.slide#44およびhttps://golang.org/ref/spec#Interface_typesを参照してください。この例でiは、今度は文字列「4.3」に再割り当てされます。i次にsi.(string)before sfを使用してfloat64型に変換され、新しい文字列変数に割り当てられますstrconv。最後fn、4に等しいint型に変換されます。型変換と型アサーションの違いは何ですか?を参照してください

Goの組み込みのマップとスライスに加えて、空のインターフェイスを使用してコンテナーを構築する機能(明示的なボックス化解除)を使用すると、多くの場合、ジェネリックが有効にするコードを記述できますが、スムーズではありません。https://golang.org/doc/faq#genericsを参照してください


インターフェイスとコードを分離します。stackoverflow.com/a/62297796/12817546を参照してください。メソッドを「動的に」呼び出します。stackoverflow.com/a/62336440/12817546を参照してください。Goパッケージにアクセスします。stackoverflow.com/a/62278078/12817546を参照してください。変数に任意の値を割り当てます。stackoverflow.com/a/62337836/12817546を参照してください。
トムJ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.