学習Go 12日目-インタフェース2、タイプ断言、内蔵インタフェース(error、Stringer、空インタフェース)
33328 ワード
インターフェース.
1.タイプブレークスルー
以前interface 1編では,interface変数は指定されたタイプの固有メソッドを使用できなかった.すなわち,Javaのようにインタフェースも実装体も存在する方法ではなく,実装体内部にのみ存在する方法がインタフェースでは利用できないようにする.
ではジャワではどうやってやったのでしょうか?タイプを変換すればいいだけです.
goのインタフェースでもタイプを変換できます.
ちなみにgoのタイプ変換は以下の通りです.
しかし、上記のコードを振り返ると、エラーが見つかります.
では、どうやって解決するのでしょうか.
この場合はタイプ断言を使用します.
インタフェースタイプの変数に特定のタイプの値が割り当てられている場合、タイプブレークスルーを使用して特定のタイプの値を取得できます.型断言方法
インタフェースで特定の方法を使用するように.これらの問題を解決するために
TapeInterfaceにはTapeRecorderだけでなくTapePlayerも含まれているからです.TapePlayerインプリメンテーションが組み込まれていますが、TapeRecorderに変換できないため問題が発生します.そのためpanicが発生します.では、タイプ断言の失敗と成功をどのように区別し、コードを柔軟にするのでしょうか.
2.タイプ断言失敗時のパニック防止
panicはコンパイル期間ではなく実行時に発生します.
このタイプの断言に失敗したことを特定するには、2番目の戻り値が成功したかどうかを決定します.boolタイプで戻り、失敗時はfalse、成功時はtrueです.
上記のpanic失敗の例を修復してみましょう.
3.インタフェース内蔵
3.1エラーインタフェース
以前Errorf()関数により過errを生成した.ここでerrはどんなタイプですか?
実際,errorインタフェースをfmtに入れると,error()メソッドが呼び出されて動作する.他の言語では、例外処理は非常に複雑であり、逆にgoはエラー処理タイプを非常に簡単に作成することができる.
3.2 Stringerインタフェース
Javaでは、toString()メソッドは継承されているので、印刷対象でも正常に印刷できます.
ただし、GoangはすべてのカスタムタイプがtoString()を継承するわけではありません.したがって,これらの機能を実現するインタフェースは正常に動作する.
3.3空のインタフェース
面白いのはfmtです.Println()にどんなカスタムタイプが入っていても、確認できます.Stringerインタフェースを実装する必要はなく、基本タイプもパラメータに入ることができます.
どうしてそんなことができるの?
それは、すべてのタイプのインタフェースが作成されているからです.それが空のインタフェースです.
このように空境界面で宣言すれば、どんなタイプのものでも受け取ることができます.
JavaのObjectのようです.任意のタイプのインタフェースを受け入れるanyというインタフェースを作成できます
この関数を使用すると、my print()関数は、どのタイプのログが受信されてもStringerのString関数を使用してログを出力します.これは不要な関数かもしれませんが、複数のサーバまたは部門間の通信またはメッセージ接続構造を作成するときに、ログが混同されると、私たちの部門か他の部門かが混同されます.このため、各部門はログを個別に出力する関数を作成します.
coffePot、teapotインスタンスを作成し、Anyタイプのスライスに入れます.そしてfor文でmy printを入れ,Anyインタフェースでfmtを自動的に受信する.Println()に移動します.
空のインタフェースでメソッドを呼び出す場合は、タイプブレークスルーを使用してそのタイプのメソッドを取得できます.
1.タイプブレークスルー
以前interface 1編では,interface変数は指定されたタイプの固有メソッドを使用できなかった.すなわち,Javaのようにインタフェースも実装体も存在する方法ではなく,実装体内部にのみ存在する方法がインタフェースでは利用できないようにする.
ではジャワではどうやってやったのでしょうか?タイプを変換すればいいだけです.
goのインタフェースでもタイプを変換できます.
ちなみにgoのタイプ変換は以下の通りです.
value_float := 1.2
value_int := int(value_float)
以前に使用した例を取得package main
import "fmt"
type TapeInterface interface {
Play(string)
Stop()
}
type TapePlayer struct {
Batteries string
}
func (t TapePlayer) Play(song string) {
fmt.Println("playing", song)
}
func (t TapePlayer) Stop() {
fmt.Println("stopped")
}
type TapeRecorder struct {
Microphones int
}
func (t TapeRecorder) Play(song string) {
fmt.Println("Recording", song)
}
func (t TapeRecorder) Record() {
fmt.Println("Recording")
}
func (t TapeRecorder) Stop() {
fmt.Println("Stopped!!")
}
func playList(device TapeInterface, songs []string) {
for _, song := range songs {
device.Play(song)
}
recorder := TapeRecorder(device)
recorder.Record()
device.Stop()
}
func main() {
player := TapePlayer{}
record := TapeRecorder{}
mixtape := []string{"first", "second", "third"}
playList(player, mixtape)
playList(record, mixtape)
}
TapeRecorderインタフェースとして宣言されたデバイスをTapeRecorderタイプのレコーダに変換し、TapeRecorderにのみ存在する一意のメソッドレコードを呼び出す.しかし、上記のコードを振り返ると、エラーが見つかります.
recorder := TapeRecorder(device)
これは、タイプ変換がインタフェースタイプに使用できないため、問題です.では、どうやって解決するのでしょうか.
この場合はタイプ断言を使用します.
インタフェースタイプの変数に特定のタイプの値が割り当てられている場合、タイプブレークスルーを使用して特定のタイプの値を取得できます.
var noiseMaker NoiseMaker = Robot("")
var robot Robot = noiseMaker.(Robot)
下図のようにnoiseMaker.(Robot)
を使用すればよい.인터페이스.(구현체타입)
です.インタフェースで特定の方法を使用するように.これらの問題を解決するために
package main
import "fmt"
type TapeInterface interface {
Play(string)
Stop()
}
type TapePlayer struct {
Batteries string
}
func (t TapePlayer) Play(song string) {
fmt.Println("playing", song)
}
func (t TapePlayer) Stop() {
fmt.Println("stopped")
}
type TapeRecorder struct {
Microphones int
}
func (t TapeRecorder) Play(song string) {
fmt.Println("Recording", song)
}
func (t TapeRecorder) Record() {
fmt.Println("Recording")
}
func (t TapeRecorder) Stop() {
fmt.Println("Stopped!!")
}
func playList(device TapeInterface, songs []string) {
for _, song := range songs {
device.Play(song)
}
recorder := device.(TapeRecorder)
recorder.Record()
device.Stop()
}
func main() {
player := TapePlayer{}
record := TapeRecorder{}
mixtape := []string{"first", "second", "third"}
playList(player, mixtape)
playList(record, mixtape)
}
上記のコードは、タイプ断言recorder := device.(TapeRecorder)
を使用してインタフェースのタイプをインプリメンテーションに変換し、特定のインプリメンテーションのメソッドを呼び出すことができる.ただし、コンパイルエラーは確実に解決されましたが、次のエラーメッセージが表示される可能性があります.panic: interface conversion: main.TapeInterface is main.TapePlayer, not main.TapeRecorder
このような間違いが発生した原因は何ですか.TapeInterfaceにはTapeRecorderだけでなくTapePlayerも含まれているからです.TapePlayerインプリメンテーションが組み込まれていますが、TapeRecorderに変換できないため問題が発生します.そのためpanicが発生します.では、タイプ断言の失敗と成功をどのように区別し、コードを柔軟にするのでしょうか.
2.タイプ断言失敗時のパニック防止
panicはコンパイル期間ではなく実行時に発生します.
このタイプの断言に失敗したことを特定するには、2番目の戻り値が成功したかどうかを決定します.boolタイプで戻り、失敗時はfalse、成功時はtrueです.
var player Player = TapePlayer{}
recorder , ok := player.(TapeRecorder)
if ok {
recorder.Record()
}else{
fmt.Println("Player was not a TapeRecorder")
}
これにより、実行時にクラッシュを防止できます.上記のpanic失敗の例を修復してみましょう.
func playList(device TapeInterface, songs []string) {
for _, song := range songs {
device.Play(song)
}
recorder, ok := device.(TapeRecorder)
if ok {
recorder.Record()
}
device.Stop()
}
次の関数に置き換えると、問題なく返されます.3.インタフェース内蔵
3.1エラーインタフェース
以前Errorf()関数により過errを生成した.ここでerrはどんなタイプですか?
err := fmt.Errorf("error hello world")
ここでは、Errorf宣言の部分に移動してみましょう.func Errorf(format string, a ...interface{}) error {}
宣言は次のように確認できます.戻りタイプはerrorですtype error interface {
Error() string
}
以下に示す.すなわち,errorはインタフェースであり,方法はerror()のみである.これにより、customerrorタイプを作成できます.実際,errorインタフェースをfmtに入れると,error()メソッドが呼び出されて動作する.他の言語では、例外処理は非常に複雑であり、逆にgoはエラー処理タイプを非常に簡単に作成することができる.
package main
import (
"fmt"
)
type overHeatError float64
func (o overHeatError) Error() string {
return fmt.Sprintf("over heat is : %0.2f", float64(o))
}
func checkTemperature(actual float64, criteria float64) error {
excess := actual - criteria
if excess > 0 {
return overHeatError(excess)
}
return nil
}
func main() {
err := checkTemperature(38.5, 37.5)
if err != nil {
fmt.Println(err)
}
}
errorインタフェースを実装するoverHeapError
を作成し、errorインタフェースを戻り値として使用できます.このようにエラーインタフェースを直接実現する実装体は,エラーがどこで発生したかをより容易に追跡できる.3.2 Stringerインタフェース
Javaでは、toString()メソッドは継承されているので、印刷対象でも正常に印刷できます.
ただし、GoangはすべてのカスタムタイプがtoString()を継承するわけではありません.したがって,これらの機能を実現するインタフェースは正常に動作する.
Stringer 인터페이스
です.type Stringer interface {
String() string
}
Stringerインタフェースの定義は次のとおりです.すなわち、String() string
の方法を実施すれば、Stringer
インターフェースを実現することができる.package main
import (
"fmt"
)
type CoffeePot string
func (c CoffeePot) String() string {
return string(c) + "coffee pot"
}
func main() {
coffeePot := CoffeePot("cold brew")
fmt.Println(coffeePot) // cold brewcoffee po
}
StringerインタフェースのString()メソッドを実装すれば、Stringerインタフェースの実装体になります.したがって、fmtメソッドにカスタムタイプが追加されても、ログは自分で実装したString()メソッドに従って出力されます.3.3空のインタフェース
面白いのはfmtです.Println()にどんなカスタムタイプが入っていても、確認できます.Stringerインタフェースを実装する必要はなく、基本タイプもパラメータに入ることができます.
どうしてそんなことができるの?
それは、すべてのタイプのインタフェースが作成されているからです.それが空のインタフェースです.
func Println(a ...interface{}) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
実際のfmt.Println()メソッドの内部宣言の様子.明確に宣言されたインタフェースではなく、可変パラメータを受け入れるinterface{}
のインタフェースがあり、簡潔なインタフェース宣言が表示されます.このように空境界面で宣言すれば、どんなタイプのものでも受け取ることができます.
JavaのObjectのようです.任意のタイプのインタフェースを受け入れるanyというインタフェースを作成できます
type Any interface{}
このようにAnyを空のインタフェースに変えて、すべてのタイプを受け入れさせます.次に、メソッドとタイプを定義します.package main
import (
"fmt"
)
type Any interface{}
type CoffeePot struct {
name string
price int
}
func (c CoffeePot) String() string {
return fmt.Sprintf("name : %s and price : %d", c.name, c.price)
}
type TeaPot struct {
name string
price int
}
func (c TeaPot) String() string {
return fmt.Sprintf("name : %s and price : %d", c.name, c.price)
}
func my_print(any Any) {
fmt.Println("my ", any)
}
func main() {
coffeePot := CoffeePot{name: "Coffee capsule", price: 10}
teaPot := TeaPot{name: "Tea capsule", price: 12}
any := []Any{coffeePot, teaPot}
for _, value := range any {
my_print(value)
}
}
StringerインタフェースのインプリメンテーションであるCoffeePot、TeaPotを作成します.さらに、Anyは空のインターフェースであり、CoffeePotとTeaPotはいずれもAnyのインプリメンテーションとしてAnyタイプに入ることができる.この関数を使用すると、my print()関数は、どのタイプのログが受信されてもStringerのString関数を使用してログを出力します.これは不要な関数かもしれませんが、複数のサーバまたは部門間の通信またはメッセージ接続構造を作成するときに、ログが混同されると、私たちの部門か他の部門かが混同されます.このため、各部門はログを個別に出力する関数を作成します.
coffePot、teapotインスタンスを作成し、Anyタイプのスライスに入れます.そしてfor文でmy printを入れ,Anyインタフェースでfmtを自動的に受信する.Println()に移動します.
空のインタフェースでメソッドを呼び出す場合は、タイプブレークスルーを使用してそのタイプのメソッドを取得できます.
Reference
この問題について(学習Go 12日目-インタフェース2、タイプ断言、内蔵インタフェース(error、Stringer、空インタフェース)), 我々は、より多くの情報をここで見つけました https://velog.io/@chappi/Go를-배워보자-12일차-인터페이스2-타입-단언-내장-인터페이스error-Stringer-빈-인터페이스テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol