iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 12
1
Software Development

Go繁不及備載系列 第 12

# Day12 Go神奇的介面與斷言(Interface, assertion)

  • 分享至 

  • xImage
  •  

Day12 Go神奇的介面與斷言(Interface, assertion)

神奇的介面

【Interface 介面】

介面是什麼?介面就是窗口、接口(不是某種電子支付)
上面有講跟沒講一樣,因為這個字詞超級無敵宇宙抽象的。

生活上會用到跟介面相關的詞語:介面活性劑、這介面好醜、...
通俗來講介面就是一個可溝通隔板,將兩者完全不同的東西區分開來,彼此卻又可以有關聯、互相影響。
常常看到的API應用程式介面,其中的I就是介面:意思是開放了功能出來,這些功能與內部程式碼實作的部分完全無關,你不知道也可以用,有點黑箱的意思(電視機內部、CPU內部對我來講都是黑箱,但是能用就對了)。

在Go語言中,interface就是任意值,可以放任何東西進去,
沒錯,就像四次元百寶袋那樣。
Google:『既然是介面,就盡量用,不要吝嗇嘛!』

https://play.golang.org/p/rBLJrC5J-dX

func main() {
	var a interface{}
	a = 123
	fmt.Println(a, reflect.TypeOf(a))
	a = "hi"
	fmt.Println(a, reflect.TypeOf(a))
	a = true
	fmt.Println(a, reflect.TypeOf(a))
}

/* result:
123 int
hi string
true bool
*/

嗯,我看到了啥...

我餵給他什麼他都能吃欸!
我餵給他int,他就變成int型態
我餵給他string,他就變成string型態
我餵給他bool,他就變成bool型態
自動轉了型態...?

interface到底是什麼型態?

func main() {
	var a interface{}
	fmt.Println(a, reflect.TypeOf(a))
}
/* result:
<nil> <nil>
 */

居然是 nil
不過這也正常,因為什麼東西都還沒給,所以還沒實體化。


除了任意值之外,interface還有一個廣泛的用法
可以定義func的名字interface之中。

你可能會問:嗯?這樣可以做什麼呢?

可以方便容易地抽層啊!不是外面賣東西抽成的抽層,
而是讓你的程式架構多一層,能讓程式更抽象化、更具有程式彈性!

譬如說,我平常刷牙都是用牙膏
我可以在interface中定義:刷起來會發生什麼事這個func
但等哪天我刷牙不想用牙膏這個介面了,
因為覺得牙膏實在太不給力,我想我可以換一個介面活性劑來使用,
我總可以換成洗碗精、或漂白水這個介面活性劑來刷牙吧!

沒錯,那屆時改程式只需實作洗碗精的func 刷起來會發生什麼事
譬如說中毒

我完全不用改動到刷牙程式,就算用漂白水也可以繼續刷牙啊!

這裡舉原本養貓,一夕之間卻改養狗的範例:

// 一開始看時,覺得寫interface之後再來引用宣告,
// 根本多此一舉,拿石頭砸自己的腳
type Animal interface {
	eat()
}

type Cat struct {
	catName string
}

func (c Cat) eat() {
	fmt.Println(c.catName, "開動哩")
}

func main() {
	var cat1 Animal = Cat{"肥貓一號"}
	cat1.eat()

	var cat2 Animal = Cat{"笨貓二號"}
	cat2.eat()
}

/* result:
肥貓一號 開動哩
笨貓二號 開動哩
*/

哪天突然想不開,覺得貓太肥太醜,很不可愛
想要棄貓養狗的時候
只需額外實作Dog.eat() 的 func、創個Dog物件
程式本體架構仍維持著高可讀性

var dog1 Animal = Dog{"開心狗一號"}
dog1.eat()

https://play.golang.org/p/F-QDduaOAz7

斷言

這裡的斷言不是打斷別人言論,
而是我敢斷言的斷言,宣稱的意思。

【Type assertions】

A type assertion provides access to an interface value's underlying concrete value.

型別斷言提供存取介面實體化的值的方法
試著把interface轉成該行別,並把值取出,用法有些特殊

func main() {
	var hello interface{} = "hello"
	helloStr := hello.(string)
	fmt.Println(helloStr)
}

/* result:
hello
*/

如果給錯型別會引發Panic

func main() {
	var hello interface{} = "hello"
	helloInt := hello.(int)
	fmt.Println(helloInt)
}

// panic: interface conversion: interface {} is string, not int

所以想在不知道型別的狀況下取出介面的型別值,
可以加上ok此一變數來驗證 動作是否成功

func main() {
	var hello interface{} = "hello"
	helloInt, ok := hello.(int)
	fmt.Println(helloInt, ok)
}

/* result:
0 false
*/

https://play.golang.org/p/MwPrTo5nOoh

【Switch Type】

可以用這個特殊的方法來檢驗一個物件的型別。

https://play.golang.org/p/FwJOD5Tvhs3

func main(){
	whatType(123)
}

func whatType(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Println("int")
	case string:
		fmt.Println("string")
	case bool:
		fmt.Println("bool")
	case nil:
		fmt.Println("nil")
	default:
		fmt.Println("unknown", v)
	}
}

為什麼說特殊呢? 因為這 i.(type) 只能搭配switch這一行所使用。
放裡面或放外面都不行,
但這種特殊用法也只在這裡會用到。
所以可以自己寫個方法來判斷所傳入的型別。


上一篇
# Day11 Go加油-韓式寒士函式(func)
下一篇
# Day13 Go怎麼傳不過去-指標的小用處(Pointer)
系列文
Go繁不及備載35
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言