# 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`