介面是什麼?介面就是窗口、接口
(不是某種電子支付)
上面有講跟沒講一樣,因為這個字詞超級無敵宇宙抽象的。
生活上會用到跟介面相關的詞語:介面活性劑、這介面好醜、...
通俗來講介面就是一個可溝通隔板
,將兩者完全不同的東西區分開來,彼此卻又可以有關聯、互相影響。
常常看到的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
這裡的斷言不是打斷別人言論,
而是我敢斷言
的斷言,宣稱
的意思。
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
可以用這個特殊的方法來檢驗一個物件的型別。
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
這一行所使用。
放裡面或放外面都不行,
但這種特殊用法也只在這裡會用到。
所以可以自己寫個方法來判斷所傳入的型別。