私はインターフェイスが初めてで、githubで SOAPリクエストを実行しようとしています
意味がわかりません
Msg interface{}
このコードでは:
type Envelope struct {
Body `xml:"soap:"`
}
type Body struct {
Msg interface{}
}
私は同じ構文を観察しました
fmt.Println
しかし、何が達成されているのか理解していない
interface{}
私はインターフェイスが初めてで、githubで SOAPリクエストを実行しようとしています
意味がわかりません
Msg interface{}
このコードでは:
type Envelope struct {
Body `xml:"soap:"`
}
type Body struct {
Msg interface{}
}
私は同じ構文を観察しました
fmt.Println
しかし、何が達成されているのか理解していない
interface{}
回答:
記事「Goでのインターフェースの使用方法」(「Russ Coxのインターフェースの説明」に基づく)を参照できます。
インターフェースとは?
インターフェースは次の2つです。
- メソッドのセットであり、
- それもタイプです
interface{}
タイプは、空のインターフェイスはないメソッドを持たないインタフェースです。implementsキーワードはないため、すべての型は少なくともゼロのメソッドを実装し、インターフェイスを満たすことは自動的に行われるため、すべての型が空のインターフェイスを満たします。
つまりinterface{}
、パラメータとして値を受け取る関数を作成する場合、その関数に任意の値を指定できます。
(それがMsg
あなたの質問で何を表しているかです:任意の値)
func DoSomething(v interface{}) {
// ...
}
ここが混乱するところです:
DoSomething
関数の中で、型は何v
ですか?初心者のホリネズミは「
v
あらゆる種類のものである」と信じさせられますが、それは間違っています。
v
タイプはありません。それはであるinterface{}
タイプ。
DoSomething
関数に値を渡すと、Goランタイムは型変換を実行し(必要な場合)、値を値に変換しinterface{}
ます。
すべての値は実行時に型を1つだけ持ち、その1v
つの静的型はinterface{}
です。インターフェース値は2ワードのデータで構成されます。
- 1つの単語は、値の基になる型のメソッドテーブルを指すために使用されます。
- もう1つの単語は、その値によって保持されている実際のデータを指すために使用されます。
補遺:これは、インターフェース構造に関してRussの記事が非常に完全なものでした:
type Stringer interface {
String() string
}
インターフェイスの値は、インターフェイスに格納されているタイプに関する情報へのポインタと関連データへのポインタを提供する2ワードのペアとして表されます。
タイプStringerのインターフェース値にbを割り当てると、インターフェース値の両方のワードが設定されます。
インターフェイス値の最初の単語は、私がインターフェイステーブルまたはitableと呼ぶものを指しています(発音はi-table。ランタイムソースでは、C実装名はItabです)。
itableは、関係する型に関するメタデータで始まり、関数ポインタのリストになります。
itableは動的タイプではなくインターフェースタイプに対応することに注意してください。
この例に関しては、のためのITableStringer
種類のバイナリを保持しているだけで満足ストリンガーに使用される方法一覧ですString
:バイナリの他のメソッドを(Get
)には外観をしませんitable
。インターフェイス値の2番目の単語は、実際のデータ、この場合はのコピーを指しています
b
。
割り当てはvar s Stringer = b
コピー可能b
ではなく、ポイントをb
同じ理由でvar c uint64 = b
あれば:コピーを作成しb
、後で変更、s
およびをc
元の値ではなく、新しいものを持っていることになっています。
インターフェイスに格納される値は任意に大きくなる可能性がありますが、インターフェイス構造で値を保持するために使用されるのは1ワードだけなので、割り当てはヒープにメモリのチャンクを割り当て、1ワードスロットにポインタを記録します。
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
}
これは空のインターフェースと呼ばれ、すべてのタイプによって実装され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"}}
これは、型がインターフェイスのすべてのメソッドを持つとすぐに型がインターフェイスを実装するという事実の論理的な拡張です。
ここにはすでに良い答えがあります。それを直感的に理解したい人のために私も追加しましょう:
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"
ご覧のとおり、不思議なことは何もありませんが、悪用は非常に簡単です。できるだけ遠ざけてください。
type Runner interface
囲碁遊び場の例では未使用です。
Golang仕様から:
インターフェイスタイプは、そのインターフェイスと呼ばれるメソッドセットを指定します。インターフェイス型の変数は、インターフェイスのスーパーセットであるメソッドセットを持つ任意の型の値を格納できます。そのようなタイプは、インターフェースを実装すると言われています。インターフェイス型の初期化されていない変数の値はnilです。
型は、そのメソッドのサブセットを含むインターフェースを実装するため、いくつかの異なるインターフェースを実装できます。たとえば、すべてのタイプは空のインターフェースを実装しています。
インターフェース{}
把握する概念は次のとおりです。
T
3つの方法があります:A
、B
、C
。T_interface = (A, B, C)
MyInterface = (A, )
MyInterface
T_interface
すべてのタイプのすべての「インターフェースタイプ」が空のインターフェースのスーパーセットであると推測できます。
@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を参照してください。I
var _ I = T{}
すべてのタイプが空のインターフェースを実装しますinterface{}
。https://talks.golang.org/2012/goforc.slide#44およびhttps://golang.org/ref/spec#Interface_typesを参照してください。この例でi
は、今度は文字列「4.3」に再割り当てされます。i
次にs
、i.(string)
before s
がf
を使用してfloat64型に変換され、新しい文字列変数に割り当てられますstrconv
。最後f
にn
、4に等しいint型に変換されます。型変換と型アサーションの違いは何ですか?を参照してください。
Goの組み込みのマップとスライスに加えて、空のインターフェイスを使用してコンテナーを構築する機能(明示的なボックス化解除)を使用すると、多くの場合、ジェネリックが有効にするコードを記述できますが、スムーズではありません。https://golang.org/doc/faq#genericsを参照してください。
interface{}
それは多かれ少なかれvoid *
Cと同等です。それは何かを指すことができ、それを使用するにはキャスト/タイプアサーションが必要です。