Golangでは、レシーバーメソッドで構造体を使用します。ここまではすべて完璧です。
しかし、インターフェイスが何なのかわかりません。構造体にメソッドを定義し、構造体にメソッドを実装する場合は、とにかく別の構造体の下にメソッドを記述します。
これは、インターフェースが単なるメソッド定義のように見えることを意味し、ページ上の余分なスペースを余分に使用します。
インターフェイスが必要な理由を説明する例はありますか?
インターフェースはここでは詳細な答えを出すにはトピックが大きすぎますが、その使用方法を明確にするためのいくつかのことがあります。
インターフェースはtoolです。それらを使用するかどうかはあなた次第ですが、コードを明確にすることができ、パッケージ間、またはクライアント(ユーザー)とサーバー(プロバイダー)の間でNice APIを提供できます。
はい、独自のstruct
タイプを作成でき、それにメソッドを「アタッチ」できます。次に例を示します。
_type Cat struct{}
func (c Cat) Say() string { return "meow" }
type Dog struct{}
func (d Dog) Say() string { return "woof" }
func main() {
c := Cat{}
fmt.Println("Cat says:", c.Say())
d := Dog{}
fmt.Println("Dog says:", d.Say())
}
_
上記のコードにはすでにいくつかの繰り返しがあります:Cat
とDog
の両方に何かを言うとき。 animalのように、両方を同じ種類のエンティティとして処理できますか?あんまり。確かに両方を_interface{}
_として処理できますが、そうする場合、_interface{}
_型の値はメソッドを定義しないため、Say()
メソッドを呼び出すことはできません。
上記の両方のタイプにいくつかのの類似性があります:両方とも同じシグネチャ(パラメーターと結果タイプ)を持つメソッドSay()
を持っています。インターフェイスでこれをcaptureできます:
_type Sayer interface {
Say() string
}
_
インターフェイスには、メソッドのsignaturesのみが含まれ、メソッドのimplementationは含まれません。
Goでは、メソッドセットがインターフェースのスーパーセットである場合、タイプが暗黙的にインターフェースを実装することに注意してください。意図の宣言はありません。これは何を意味するのでしょうか?以前のCat
およびDog
型は、このSayer
インターフェイスを既に実装していますが、このインターフェイスの定義は、以前に記述したときにも存在していなかったため、それらまたは何かをマークします。彼らはただやる。
インターフェイスは動作を指定します。インターフェースを実装する型は、その型がインターフェースが「規定する」すべてのメソッドを持っていることを意味します。
両方ともSayer
を実装しているため、両方をSayer
の値として扱うことができ、共通しています。両方を統一して処理する方法をご覧ください。
_animals := []Sayer{c, d}
for _, a := range animals {
fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}
_
(これは、タイプ名を取得することだけを反映しているので、今のところあまり使わないでください。)
重要な部分は、Cat
とDog
の両方を同じ種類(インターフェイスタイプ)として処理し、それらを操作/使用できることです。 Say()
メソッドを使用して追加の型をすばやく作成する場合は、Cat
およびDog
の横に並べることができます。
_type Horse struct{}
func (h Horse) Say() string { return "neigh" }
animals = append(animals, Horse{})
for _, a := range animals {
fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}
_
これらの型で動作する他のコードを書きたいとしましょう。ヘルパー関数:
_func MakeCatTalk(c Cat) {
fmt.Println("Cat says:", c.Say())
}
_
はい、上記の関数はCat
でのみ機能します。似たようなものが必要な場合は、タイプごとに記述する必要があります。これがどれほど悪いかは言うまでもない。
はい、_interface{}
_の引数を取るように記述し、 type assertion または type Switches を使用できます。これにより、ヘルパー関数の数が減りますが、本当にいです。
ソリューション?はい、インターフェース。関数を宣言して、それを使用したい動作を定義するインターフェイスタイプの値を取得します。それがすべてです。
_func MakeTalk(s Sayer) {
fmt.Println(reflect.TypeOf(s).Name(), "says:", s.Say())
}
_
この関数は、Cat
、Dog
、Horse
、またはSay()
メソッドを持つ '今まで知らない他のタイプの値で呼び出すことができます。涼しい。
Go Playground でこれらの例を試してください。
インターフェイスはいくつかの種類のジェネリックを提供します。アヒルのタイピングについて考えます。
type Reader interface{
Read()
}
func callRead(r Reader){
r.Read()
}
type A struct{
}
func(_ A)Read(){
}
type B struct{
}
func(_ B)Read(){
}
構造体A
とB
をcallRead
に渡すことは問題ありません。どちらもReaderインターフェースを実装しているからです。ただし、インターフェイスがない場合は、A
とB
の2つの関数を記述する必要があります。
func callRead(a A){
a.Read()
}
func callRead2(b B){
b.Read()
}
ここでは、Goのインターフェイスの2つの興味深い使用例を示します。
1-次の2つのシンプルなインターフェースをご覧ください。
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
これらの2つのシンプルなインターフェイスを使用して、この興味深い魔法を実行できます。
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"strings"
)
func main() {
file, err := os.Create("log.txt")
if err != nil {
panic(err)
}
defer file.Close()
w := io.MultiWriter(file, os.Stdout)
r := strings.NewReader("You'll see this string twice!!\n")
io.Copy(w, r)
slice := []byte{33, 34, 35, 36, 37, 38, 39, 10, 13}
io.Copy(w, bytes.NewReader(slice)) // !"#$%&'
buf := &bytes.Buffer{}
io.Copy(buf, bytes.NewReader(slice))
fmt.Println(buf.Bytes()) // [33 34 35 36 37 38 39 10 13]
_, err = file.Seek(0, 0)
if err != nil {
panic(err)
}
r = strings.NewReader("Hello\nWorld\nThis\nis\nVery\nnice\nInterfacing.\n")
rdr := io.MultiReader(r, file)
scanner := bufio.NewScanner(rdr)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
出力:
You'll see this string twice!!
!"#$%&'
[33 34 35 36 37 38 39 10 13]
Hello
World
This
is
Very
Nice
Interfacing.
You'll see this string twice!!
!"#$%&'
このコードが十分に明確であることを願っています:strings.NewReader
を使用して文字列から読み取り、io.Copy(w, r)
だけでos.Stdout
を使用してfile
とio.MultiWriter
の両方に同時に書き込みます。次に、bytes.NewReader(slice)
を使用してスライスから読み取り、file
とos.Stdout
の両方に同時に書き込みます。次に、スライスをバッファio.Copy(buf, bytes.NewReader(slice))
にコピーし、file.Seek(0, 0)
を使用してOriginファイルに移動し、strings.NewReader
を使用して文字列から最初に読み取り、io.MultiReader(r, file)
を使用してfile
]およびbufio.NewScanner
およびfmt.Println(scanner.Text())
を使用してすべてを印刷します。
2-そして、これはインターフェイスの別の興味深い使用法です:
package main
import "fmt"
func main() {
i := show()
fmt.Println(i) // 0
i = show(1, 2, "AB", 'c', 'd', []int{1, 2, 3}, [...]int{1, 2})
fmt.Println(i) // 7
}
func show(a ...interface{}) (count int) {
for _, b := range a {
if v, ok := b.(int); ok {
fmt.Println("int: ", v)
}
}
return len(a)
}
出力:
0
int: 1
int: 2
7
素敵な例: Goのタイプアサーションの説明
次も参照してください: Go:インターフェイスの意味は何ですか{}?
構造体に関係なくメソッドを実装する必要がある場合。
ローカルの構造体にアクセスし、構造体を知る前にハンドラーを使用するハンドラーメソッドがある場合があります。
他の構造体または現在の構造体に固有の動作が必要な場合。
ユーザーがインターフェースを使用しない可能性があるため、いくつかの方法でインターフェースを表示したい場合があります。ユースケースごとに構造体を分割したい場合があります。
何かを実装する型が必要な場合。
あなたはタイプを知っているかもしれませんが、少なくとも値は持っています。