# Day 24 : Golang interface(2) - interface{} & type assertion ## interface{} 在寫Go的時候可能大家偶爾會用fmt.Println()來debug,但有沒有想過Go明明是一種強型別的語言(strongly typed language),即一旦變數被定義型別,如果沒有強制轉換,就只能維持該型別,那為什麼不管我丟入任何型別的變數,都可以透過`fmt.Println()`得到該變數的值呢? 如果有定義是要傳入string型別的函式,就一定要將原本定義的int型別透過strconv package的方法轉為string ```go= func main() { num := 7 numString := strconv.Itoa(num) printString(numString) // 7 } func printString(s string) { fmt.Println(s) } ``` 但fmt.Println()可以接受任意型別 ```go= func main() { a := 123 fmt.Println(a) // 123 b := "string" fmt.Println(b) // string c := true fmt.Println(c) // true } ``` 要瞭解這個疑問,可以先看看Println()在fmt package原始文件的定義 ``` func Println(a ...any) (n int, err error) { return Fprintln(os.Stdout, a...) } ``` `...`代表可以接受任意數量的參數,any定義在另一個內建的src package裡面,其為`type any = interface{}`。這時參數的型別是一個介面,那麼任何變數的型別只要符合該介面就可以傳入,特別的是interface{}沒有定義任何函式,並沒有要求型別需要具有特定方法,所以任何型別都會符合這個介面,這也是為什麼`fmt.Println()`可以接受任意型別作為參數。 換個使用宣告型別interface{}的寫法,為空介面可以存儲任何值的類型,所以它可以被賦值為int、string、bool等不同的類型。 ```go= func main() { var a interface{} a = 123 fmt.Println(a) // 123 a = "string" fmt.Println(a) // string a = true fmt.Println(a) // true } ``` 但當你用自己定義的結構體型別當作變數傳入函式,該變數如果有自己的欄位或是方法的話,是無法讀取的,這時候就要用到type assertion。 ## type assertion 這裡稍微修改前一天的例子,假設建立一個Dog的結構體,並定義了屬於Dog的Sleep()、Play()、Eat()三個方法,在main函式裡面,將變數i定義成一個空介面,並將一個Pet的例子存入i變數,此時第31、32行是不可以執行的。 ```GO= package main import "fmt" type Pet interface { Sleep() string Play() string Eat() string } type Dog struct { name string } func (d Dog) Sleep() string { return fmt.Sprintf("I am %s, I am sleeping", d.name) } func (d Dog) Play() string { return fmt.Sprintf("I am %s, I am having fun", d.name) } func (d Dog) Eat() string { return fmt.Sprintf("I am %s, I am eating", d.name) } func main() { var i interface{} i = Dog{"White"} fmt.Println(i.Sleep()) // error fmt.Println(i.Eat()) // error } ``` 因為這時候Go會要求遵循空介面的規定,你只能存取介面裡面列出的方法,而因為interface{}裡面又沒有方法,所以上面`i.Sleep()`會出現錯誤。這時候要透過`type assertion`。在第27行使用`type assertion`將原本**空介面型別的i轉換成Pet型別**,並存入變數p中,這樣就可以使用原本Pet結構體定義的方法了。 ```GO= type Pet interface { Sleep() string Play() string Eat() string } type Dog struct { name string } func (d Dog) Sleep() string { return fmt.Sprintf("I am %s, I am sleeping", d.name) } func (d Dog) Play() string { return fmt.Sprintf("I am %s, I am having fun", d.name) } func (d Dog) Eat() string { return fmt.Sprintf("I am %s, I am eating", d.name) } func main() { var i interface{} i = Dog{"White"} p := i.(Pet) fmt.Println(p.Sleep()) // I am White, I am sleeping fmt.Println(p.Eat()) // I am White, II am eating } ``` ## References 1. [Golang Conversions - Ints To Strings And Strong Typing](https://blog.boot.dev/golang/golang-conversions-ints-to-strings-and-strong-typing/) 2. [fmt package](https://pkg.go.dev/fmt#Println) 3. [[Golang] interfaces](https://pjchender.dev/golang/interfaces/) ###### tags: `About Go`