サーチ…
備考
Goのインターフェースは固定されたメソッドセットです。型は、そのメソッドセットがインタフェースのスーパーセットである場合、そのインタフェースを暗黙的に実装します。 意図の宣言はありません。
シンプルなインターフェイス
Goでは、インターフェイスは単なる一連のメソッドです。インターフェイスを使用して、指定されたオブジェクトの動作を指定します。
type Painter interface {
Paint()
}
実装する型は、インタフェースを実装していると宣言する必要はありません 。同じ署名のメソッドを定義すれば十分です。
type Rembrandt struct{}
func (r Rembrandt) Paint() {
// use a lot of canvas here
}
これで構造体をインタフェースとして使用できるようになりました。
var p Painter
p = Rembrandt{}
インタフェースは、任意の数の型で満たすことができます(または実装できます)。また、型は任意の数のインタフェースを実装できます。
type Singer interface {
Sing()
}
type Writer interface {
Write()
}
type Human struct{}
func (h *Human) Sing() {
fmt.Println("singing")
}
func (h *Human) Write() {
fmt.Println("writing")
}
type OnlySinger struct{}
func (o *OnlySinger) Sing() {
fmt.Println("singing")
}
ここでは、 Human
構造体はSinger
とWriter
両方のインタフェースを満たしていますが、 OnlySinger
構造体はSinger
インタフェースのみを満たしています。
空のインターフェイス
メソッドを持たない空のインターフェイス型があります。私たちはそれをinterface{}
と宣言しinterface{}
。これはすべてのtype
がそれを満たすようにメソッドを含んでいません。したがって、空のインターフェイスには任意の型の値を含めることができます。
var a interface{}
var i int = 5
s := "Hello world"
type StructType struct {
i, j int
k string
}
// all are valid statements
a = i
a = s
a = &StructType{1, 2, "hello"}
インターフェイスの最も一般的な使用例は、変数が1つまたは複数の動作をサポートすることを保証することです。対照的に、空のインタフェースの主な使用例は、具体的な型にかかわらず、任意の値を保持できる変数を定義することです。
これらの値をオリジナルの型として戻すには、単に行う必要があります
i = a.(int)
s = a.(string)
m := a.(*StructType)
または
i, ok := a.(int)
s, ok := a.(string)
m, ok := a.(*StructType)
ok
は、 interface a
が指定の型に変換可能かどうかを示しinterface a
。それはキャストすることができない場合ok
になりますfalse
。
インタフェース値
インターフェイスの変数を宣言すると、インターフェイスによって宣言されたメソッドを実装する任意の値の型を格納することができます。
interface Singer
h
を宣言すると、 Human
またはOnlySinger.
型の値が格納されますOnlySinger.
これは、すべてがSinger
インターフェイスで指定されたメソッドを実装しているためです。
var h Singer
h = &human{}
h.Sing()
インタフェースから基礎となる型を決定する
外出先では、渡された基礎となるタイプを知ることが有用な場合があります。これは、タイプスイッチで行うことができます。これは2つの構造体があることを前提としています:
type Rembrandt struct{} func (r Rembrandt) Paint() {} type Picasso struct{} func (r Picasso) Paint() {}
Painterインターフェースを実装しています:
type Painter interface { Paint() }
次に、このスイッチを使用して、基になる型を判断することができます。
func WhichPainter(painter Painter) { switch painter.(type) { case Rembrandt: fmt.Println("The underlying type is Rembrandt") case Picasso: fmt.Println("The underlying type is Picasso") default: fmt.Println("Unknown type") } }
型がインタフェースを満たすかどうかのコンパイル時チェック
インタフェースと実装(インタフェースを実装する型)は「分離」されています。コンパイル時に型がインタフェースを実装しているかどうかをチェックする方法は正当な疑問です。
型T
がインタフェースI
実装していることをコンパイラに確認する方法の1つは、 T
のゼロ値またはT
へのポインタを適切に使用して代入を試みることです。また、不要なガーベジを避けるために空白の識別子に割り当てることもできます。
type T struct{}
var _ I = T{} // Verify that T implements I.
var _ I = (*T)(nil) // Verify that *T implements I.
T
または*T
がI
実装していない場合、コンパイル時エラーとなります。
この質問は正式なFAQにも現れています: 私の型がインターフェイスを満たすことをどうすれば保証できますか?
タイプスイッチ
タイプスイッチは、ケースのタイプに一致する変数を取得するためにも使用できます。
func convint(v interface{}) (int,error) {
switch u := v.(type) {
case int:
return u, nil
case float64:
return int(u), nil
case string:
return strconv(u)
default:
return 0, errors.New("Unsupported type")
}
}
タイプアサーション
タイプアサーションを使用して、実際のデータ型のインターフェイスにアクセスできます。
interfaceVariable.(DataType)
構造体の例MyType
インタフェースの実装Subber
:
package main
import (
"fmt"
)
type Subber interface {
Sub(a, b int) int
}
type MyType struct {
Msg string
}
//Implement method Sub(a,b int) int
func (m *MyType) Sub(a, b int) int {
m.Msg = "SUB!!!"
return a - b;
}
func main() {
var interfaceVar Subber = &MyType{}
fmt.Println(interfaceVar.Sub(6,5))
fmt.Println(interfaceVar.(*MyType).Msg)
}
.(*MyType)
なければ、 Msg
Fieldにアクセスすることはできません。 interfaceVar.Msg
を試すと、コンパイルエラーが表示されます:
interfaceVar.Msg undefined (type Subber has no field or method Msg)
数学的な面からのインタフェース
数学、特に集合理論では、 集合と呼ばれるものの集合を持ち、それらを要素と名づけます 。 A、B、C、...のような名前を持つセットを表示したり、そのメンバーを{a、b、c、d、e}の中かっこに明示して明示的に表示します。任意の要素xと集合Zがあるとします。重要な質問は、「xがZのメンバーであるかどうかわかりますか」です。数学者の答えは、この質問に対する概念:集合の特徴的な性質 。 Setの特性Propertyは、setを完全に記述する式です。たとえば、{0,1,2,3,4,5、...}の自然数のセットがあります。この集合を以下の式で記述することができる:{a n | a 0 = 0、a n = a n-1 +1}である。最後の式において、a 0 = 0であり、a n = a n-1 +1は、自然数の組の特性特性である。 この式があれば、この集合を完全に構築することができます。このようにして偶数の集合を記述しましょう。この集合は{0、2、4、6、8、10、...}の数で作られていることが分かります。一見すると、この数字のすべてが自然数であることを理解しています。つまり、自然数の特性プロパティにいくつかの条件を追加すると、このセットを記述する新しい式を作成できます。だから私たちはこの表現で説明することができます:{n | nは自然数のメンバーであり 、2のnのリマインダーはゼロです}。今度は、セットの特性プロパティを取得し、いくつかの望ましいエレメントをフィルタリングして、セットの要素を返すフィルタを作成できます。たとえば、自然数フィルタを使用すると、自然数と偶数の両方がこのフィルタを通過できますが、偶数のフィルタを使用すると、3や137871などのいくつかの要素はフィルタを通過できなくなります。
Goのインタフェースの定義は、特性のプロパティを定義するようなものであり、インタフェースを関数の引数として使用する仕組みは、要素を検出するフィルタのようなものであり、私たちが望むセットのメンバーであるかどうかです。コードでこの側面を説明しましょう:
type Number interface {
IsNumber() bool // the implementation filter "meysam" from 3.14, 2 and 3
}
type NaturalNumber interface {
Number
IsNaturalNumber() bool // the implementation filter 3.14 from 2 and 3
}
type EvenNumber interface {
NaturalNumber
IsEvenNumber() bool // the implementation filter 3 from 2
}
Number
の特性はすべてIsNumber
メソッドを持つ構造体ですNaturalNumber
はIsNumber
とIsNaturalNumber
メソッドを持つもので、 EvenNumber
は最後にIsNumber
、 IsNaturalNumber
、およびIsEvenNumber
メソッドを持つすべての型です。このインタフェースの解釈のおかげで、 interface{}
は特性値がないので、すべての型を受け入れることができます(値を区別するためのフィルタがないため)。