ホームページ >バックエンド開発 >Golang >Go言語のインターフェースタイプを変換する方法

Go言語のインターフェースタイプを変換する方法

青灯夜游
青灯夜游オリジナル
2023-01-11 11:41:511852ブラウズ

Go 言語は、型アサーションを使用してインターフェイス型を実行できます。 Go では、あるインターフェイス型を別のインターフェイス型に変換する場合でも、インターフェイスを別の基本型に変換する場合でも、型アサーションを使用する必要があります。「変換された変数: = インターフェイス変数。(ターゲット型)」という 2 種類の変換構文があります。および「変換された変数、ok := インターフェイス変数。(ターゲットの型)」。

Go言語のインターフェースタイプを変換する方法

このチュートリアルの動作環境: Windows 7 システム、GO バージョン 1.18、Dell G3 コンピューター。

Golang では、あるインターフェイス型を別のインターフェイス型に変換したり、インターフェイスを別の基本型に変換したりするには、Type アサーション を使用する必要があります。

型アサーションの形式

型アサーションは、インターフェイス値に対して使用される操作です。構文的には、i.(T) はアサーション タイプと呼ばれるように見えます。ここで、i はインターフェイス タイプを表し、T はタイプを表します。型アサーションは、操作対象のオブジェクトの動的型がアサートされた型と一致するかどうかをチェックします。

型アサーションの基本形式は次のとおりです。

t := i.(T)

ここで、i はインターフェイス変数を表し、T は変換されたターゲットの型を表し、t は変換された変数を表します。

ここでは 2 つの可能性があります。まず、アサートされた型 T が具象型の場合、型アサーションは i の動的型が T と同じかどうかをチェックします。このチェックが成功すると、型アサーションの結果は i の動的な値になります。これはもちろん型 T です。つまり、具象型の型アサーションは、そのオペランドから具象値を取得します。チェックが失敗すると、後続の操作でパニックが発生します。例:

var w io.Writer
w = os.Stdout
f := w.(*os.File) // 成功: f == os.Stdout
c := w.(*bytes.Buffer) // 死机:接口保存*os.file,而不是*bytes.buffer

Second、アサートされた型 T がインターフェイス型の場合、型アサーションは i の動的型が T を満たすかどうかをチェックします。このチェックが成功した場合、動的値は取得されません。結果は同じ型と値部分を持つインターフェイス値のままですが、結果の型は T になります。つまり、インターフェイス型の型アサーションは、型の表現方法を変更し、利用可能なメソッドのセット (通常はより大きい) を変更しますが、インターフェイス値の動的な型と値の部分は保護されます。

以下の最初の型アサーションの後、w と rw は両方とも os.Stdout を保持するため、それぞれ動的型 *os.File を持ちますが、変数 w は外部にのみ公開される io.Writer 型です。ファイルの Write メソッドは公開されますが、rw 変数は Read メソッドのみを公開します。

var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) // 成功:*os.file具有读写功能
w = new(ByteCounter)
rw = w.(io.ReadWriter) // 死机:*字节计数器没有读取方法

アサーション操作のオブジェクトが nil インターフェイス値の場合、アサートされる型に関係なく、型アサーションは失敗します。 nil インターフェイス値を除いて、代入操作のように動作するため、制限の少ないインターフェイス タイプ (メソッドのセットが少ない) をアサートする必要はほとんどありません。

T インターフェイスのメソッドを完全に実装していない場合、このステートメントはクラッシュを引き起こします。ダウンタイムを引き起こすのはあまり好ましくないので、上記のステートメントを記述する別の方法があります:

t,ok := i.(T)

この方法では、インターフェイスが実装されていない場合、ok は false に設定され、t は に設定されます。タイプ T の 0。値。通常の実装では、ok は true です。ここでの ok は、インターフェイス i が型 T を実装するかどうかの結果と考えることができます。

#インターフェースを他のインターフェースに変換する

#あるインターフェースを実装する型は、別のインターフェースも実装します。このとき、2 つのインターフェースは間の変換が可能です。

鳥と豚には異なる特性があり、鳥は飛べますが、豚は飛べませんが、どちらの動物も歩くことができます。構造体を使用して鳥と豚を実装する場合、独自の特性を与える Fly() メソッドと Walk() メソッドを使用すると、鳥と豚にそれぞれ飛行動物インターフェイス (Flyer) と歩行動物インターフェイス (Walker) を実装できます。

鳥と豚のインスタンスが作成された後、それらはインターフェース タイプのマップに保存されます。{} Interface{} タイプは空のインターフェースを表します。つまり、このインターフェースは任意のタイプとして保存できます。 Bird または Pig のインスタンスを保持するインターフェース{} 変数に対してアサーション操作を実行します。アサーション オブジェクトがアサーションで指定されたタイプの場合は、アサーション オブジェクトのタイプに変換されたインターフェースが返されます。指定されたアサーション タイプでない場合は、アサーション オブジェクトのタイプに変換されたインターフェースが返されます。の場合、アサーションの 2 番目のパラメーターは false を返します。

たとえば、次のコード:

var obj interface = new(bird)
f, isFlyer := obj.(Flyer)

コード内では、new(bird) は *bird 型の Bird インスタンスを生成し、このインスタンスは、interface{ 型の obj 変数に保存されます。 }。 obj.(Flyer) 型アサーションを使用して、obj を Flyer インターフェイスに変換します。 f は変換が成功した場合の Flyer インターフェイスの型で、isFlyer は変換が成功したかどうかを示し、型は bool です。

以下は詳細なコード (コード 1) です。

package main
import "fmt"
// 定义飞行动物接口
type Flyer interface {
    Fly()
}
// 定义行走动物接口
type Walker interface {
    Walk()
}
// 定义鸟类
type bird struct {
}
// 实现飞行动物接口
func (b *bird) Fly() {
    fmt.Println("bird: fly")
}
// 为鸟添加Walk()方法, 实现行走动物接口
func (b *bird) Walk() {
    fmt.Println("bird: walk")
}
// 定义猪
type pig struct {
}
// 为猪添加Walk()方法, 实现行走动物接口
func (p *pig) Walk() {
    fmt.Println("pig: walk")
}
func main() {
// 创建动物的名字到实例的映射
    animals := map[string]interface{}{
        "bird": new(bird),
        "pig":  new(pig),
    }
    // 遍历映射
    for name, obj := range animals {
        // 判断对象是否为飞行动物
        f, isFlyer := obj.(Flyer)
        // 判断对象是否为行走动物
        w, isWalker := obj.(Walker)
        fmt.Printf("name: %s isFlyer: %v isWalker: %v\n", name, isFlyer, isWalker)
        // 如果是飞行动物则调用飞行动物接口
        if isFlyer {
            f.Fly()
        }
        // 如果是行走动物则调用行走动物接口
        if isWalker {
            w.Walk()
        }
    }
}

コードの説明は次のとおりです。

  • 行 6 は、空を飛ぶ動物。

  • 行 11 は、歩く動物のインターフェイスを定義します。

  • 行 16 と行 30 はそれぞれ鳥と豚のオブジェクトを定義し、それぞれ飛行動物と歩行動物のインターフェイスを実装しています。

  • 行 41 はオブジェクト名とオブジェクト インスタンスをマッピングするマップで、インスタンスは鳥と豚です。

  • 行 47 はマップの移動を開始します。obj はインターフェースのタイプです。{}

  • 第 50 行中,使用类型断言获得 f,类型为 Flyer 及 isFlyer 的断言成功的判定。

  • 第 52 行中,使用类型断言获得 w,类型为 Walker 及 isWalker 的断言成功的判定。

  • 第 57 和 62 行,根据飞行动物和行走动物两者是否断言成功,调用其接口。

代码输出如下:

Go言語のインターフェースタイプを変換する方法

将接口转换为其他类型

在代码 1 中,可以实现将接口转换为普通的指针类型。例如将 Walker 接口转换为 *pig 类型,请参考下面的代码:

p1 := new(pig)
var a Walker = p1
p2 := a.(*pig)
fmt.Printf("p1=%p p2=%p", p1, p2)

对代码的说明如下:

  • 第 3 行,由于 pig 实现了 Walker 接口,因此可以被隐式转换为 Walker 接口类型保存于 a 中。

  • 第 4 行,由于 a 中保存的本来就是 *pig 本体,因此可以转换为 *pig 类型。

  • 第 6 行,对比发现,p1 和 p2 指针是相同的。

如果尝试将上面这段代码中的 Walker 类型的 a 转换为 *bird 类型,将会发出运行时错误,请参考下面的代码:

p1 := new(pig)
var a Walker = p1
p2 := a.(*bird)

运行时报错:

panic: interface conversion: main.Walker is *main.pig, not *main.bird

报错意思是:接口转换时,main.Walker 接口的内部保存的是 *main.pig,而不是 *main.bird。

因此,接口在转换为其他类型时,接口内保存的实例对应的类型指针,必须是要转换的对应的类型指针。

总结

接口和其他类型的转换可以在Go语言中自由进行,前提是已经完全实现。

接口断言类似于流程控制中的 if。但大量类型断言出现时,应使用更为高效的类型分支 switch 特性。

【相关推荐:Go视频教程编程教学

以上がGo言語のインターフェースタイプを変換する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。