首頁 > 後端開發 > Golang > 主體

go語言介面類型怎麼轉換

青灯夜游
發布: 2023-01-11 11:41:51
原創
1740 人瀏覽過

go語言可利用型別斷言來進行介面類型。在Go中,無論是將一個介面類型轉換成另一個介面類型,或是將一個介面轉換為另一個基本類型,都必須使用類型斷言;轉換語法有兩種「轉換後的變數:= 介面變數. (目標類型)」和「轉換後的變數, ok := 介面變數.(目標類型)」。

go語言介面類型怎麼轉換

本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。

在 Golang 中,將一個 介面 型別轉換成另一個介面 型,或是將一個介面轉換為另一個基本型別,都必須使用 類型斷言

類型斷言的格式

類型斷言是一個使用在介面值上的操作。語法上它看起來像 i.(T) 被稱為斷言類型,這裡 i 表示一個介面的型別和 T 表示一個型別。一個類型斷言檢查它操作物件的動態類型是否和斷言的類型匹配。

類型斷言的基本格式如下:

t := i.(T)
登入後複製

其中,i 代表介面變量,T 代表轉換的目標類型,t 代表轉換後的變數。

這裡有兩種可能。第一種,如果斷言的類型 T 是具體類型,然後類型斷言檢查 i 的動態類型是否和 T 相同。如果這個檢查成功了,型別斷言的結果是 i 的動態值,當然它的型別是 T。換句話說,具體類型的類型斷言從它的操作物件中獲得具體的值。如果檢查失敗,接下來這個操作會拋出 panic。例如:

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

第二種,如果相反斷言的類型 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 介面值的情況。

如果 i 沒有完全實作 T 介面的方法,這個語句將會觸發宕機。觸發宕機不是很友好,因此上面的語句還有一種寫法:

t,ok := i.(T)
登入後複製

這種寫法下,如果發生接口未實現時,將會把ok 置為false,t 置為T 類型的0值。正常實作時,ok 為 true。這裡 ok 可以被認為是:i 介面是否實作 T 類型的結果。

將介面轉換為其他接口

實作某個介面的型別同時實作了另外一個接口,此時可以在兩個接口間轉換。

鳥和豬有不同的特性,鳥可以飛,豬不能飛,但兩種動物都可以行走。如果使用結構體實現鳥和豬,讓它們具備自己特性的 Fly() 和 Walk() 方法就讓鳥和豬各自實現了飛行動物介面(Flyer)和行走動物介面(Walker)。

將鳥和豬的實例建立後,被儲存到 interface{} 類型的 map 中。 interface{} 類型表示空接口,意思是這種接口可以保存為任意類型。對保存有鳥或豬的實例的interface{} 變數進行斷言操作,如果斷言物件是斷言指定的類型,則傳回轉換為斷言物件類型的介面;如果不是指定的斷言類型時,斷言的第二個參數將返回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 行是一個 map,映射物件名字和物件實例,實例是鳥和豬。

  • 第 47 行開始遍歷 map,obj 為 interface{} 介面類型。

  • 第 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中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!