Day8 Function and Interface

Function

func (basedMethod) funcName(parameters)(returnType) {
}

Go的func命名是采用驼峰式大小写,譬如:FindTheJob

字首大写代表对外暴露,是个global function。

字首小写则只能在相同package中使用。

无回传值的func

func Variable() {
...
}

package main

import (
	"fmt"
)

func main() {
	a()
}

func a() {
	fmt.Println("Hello World")
}

运行後可得以下结果

Hello World

有回传值的func

回传一个变数,要先定义回传的变数型态

func Variable() ReturnType {
...
}

package main

import (
	"fmt"
)

func main() {
	fmt.Println(b())
}

func a() {
	fmt.Println("Hello World")
}

func b() int {
	return 1 + 2
}

运行後可得以下结果

3

有回传值且预先宣告变数名称的func

func Variable() (Variable ReturnType) {
...
}

等同於在func中做了var variable type这件事

所以func内的variable可以直接取用

func d() (e string) {
	e = "I am e!"
	return e
}

多个回传值的func

func Variable() (ReturnType, ReturnType) {
...
}

package main

import (
	"fmt"
)

func main() {
	fmt.Println(c())
}
a
func a() {
	fmt.Println("Hello World")
}

func b() int {
	return 1 + 2
}

func c() (int, string) {
	return 1 + 2, "参"
}

运行後可得结果

3 参

Struct中的Func

Struct搭配Func

func (S Struct) Variable() {
...
}

package main

import (
	"fmt"
)

type Bag struct {
	Wallet string
	Pen string
}

func main() {
	var bag Bag
	bag.Pen = "BlackPen"
	bag.show()
}

func a() {
	fmt.Println("Hello World")
}

func b() int {
	return 1 + 2
}

func c() (int, string) {
	return 1 + 2, "参"
}

func (Bag *Bag) show() {
	pen := Bag.Pen
	fmt.Println(pen) 
}

运行後可得以下结果

BlackPen

这方法让你的物件Bag 动了起来,

他不在只是个物件,他现在会show时展示笔的资讯。

Interface

Go的Interface类型是对其他类型行为的抽象和概括,因为Interface不会和特定的实现细节绑定一起透过该种抽象方式可以让我们对对象更加灵活和更具有适应能力。

Go中的interface可以是任意值。

package main

import (
	"fmt"
)

type Area interface {
    Calculate() float64
}

type Rect struct {
    width  float64
    height float64
}

func (r Rect) Calculate() float64 {
    return r.height * r.width
}

func main() {
    var a Area
    fmt.Printf("(%T, %v) \n", a, a)
    a= Rect{3, 5}
    fmt.Printf("(%T, %v) \n", a, a)
    fmt.Println(a.Calculate())
}

运行後可得以下结果

(<nil>, <nil>) 
(main.Rect, {3 5}) 
15

我们可以得知

  • Area这个interface刚实体化时type与value皆为nil
  • 之後赋予其struct後type为Rect
  • 之後便才可以使用Calculate(),若在赋予struct前执行Calculate()便会抱错。

下面我们再看一个生动的例子,让大家更加了解interface

package main

import (
	"fmt"
)

type Animal interface {
	eat()
}

type Cat struct {
	catName string
}

func (c Cat) eat() {
	fmt.Println(c.catName, "Eat!!")
}

func main() {
	var cat1 Animal = Cat{"Black Cat"}
	cat1.eat()

	var cat2 Animal = Cat{"White Cat"}
	cat2.eat()
}

运行後可得以下结果

Black Cat Eat!!
White Cat Eat!!

Type assertions

型别断言 提供存取介面实体化值的方法

试着将interface转成该型别,并把值取出

package main

import (
	"fmt"
)

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

运行後可得以下结果

hello

如果给错型别会引发Panic

package main

import (
	"fmt"
)

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

运行後会得以下结果

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

goroutine 1 [running]:
main.main()
	/tmp/sandbox1366433049/prog.go:9 +0x2e

所以想在不知道型别的状况下取出介面的型别值,

可以加上ok此一变数来验证 动作是否成功

package main

import (
	"fmt"
)

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

运行後可得以下结果

0 false

Switch Type

可以用这个特殊的方法来检验一个物件的型别。

package main

import (
	"fmt"
)

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)
	}
}

运行後可得以下结果

int

i.(type)只能够搭配switch使用,因此特殊。

Polymorphism

当一个type实作了interface後,该type所建立的变数除了属於原本type外,也属於该interface的type,一个变数同时符合多个型别就被称为Polymorphism 多型。

package main

import (
	"fmt"
)

type Answer interface {
    getSalary() int
}

type Question struct {
    a, b int
}

func (q Question) Add() int {
    return q.a + q.b
}

type Student struct {
    firstName, lastName string
    question              Question
}

func main() {
    flynn := Student{
        firstName: "Flynn",
        lastName:  "Sun",
        question: Question{
            30, 20,
        },
    }

    fmt.Println("Student's Answer", flynn.question.Add())
}

举例来说,这里先定义了 Salaried 这个 interface type,接着 Salary 这个 struct type 实作了 Salaried 中定义的 method signatures,因此 Salary 这个 struct type 也同时符合了 Salaried interface type,这样的行为称作 polymorphism。

举例来说,这里我们

  1. 定义了Answer这个Interface type
  2. 接下来 Question这个struct type实作了Answer中的method signatures
  3. 所以Question的struct type同时也符合了Answer interface type

这行为被称作polymorphism

Standard Example

综合以上好处,interface在Golnag的标准库当中是非常常用到的,像是定义档案的读写的Reader, Writer interface等

type Reader interface {
	Read(p []byte) (n int, err error)
}

type Writer interface {
	Write(p []byte) (n int, err error)
}

两者皆利用了os.File实现其interface,已完成档案的读与写

Summary

Function与Interface基本上已经是Go开发中不可或缺的元素了,尤其是Function!

但这边介绍的用法也只是冰山一角,希望大家入门後可以再透过实作与练习再去更深入它。


<<:  [Day - 08] - Spring 通用注入各式元件运作与设计

>>:  Day8 - 2D渲染环境基础篇 IV[像素操作概论] - 成为Canvas Ninja ~ 理解2D渲染的精髓

# JS杂食-06--小实作-1: Star Calculator

参考资料1:MDN — the Mozilla Developer Network 参考资料2:0...

D4 - 加盐不加价 严格模式开启

前言 JavaScript 相较是个自由的语言,在学习语法时会发现,咦 明明规则是这样,怎麽那样也可...

Day04: 04 - 页面刻划(3) -商品详情、订单详情、个人资料

Hi,안녕하세요,我是Charlie! 在Day03当中,我们完成了登入、注册跟订单页面,而今天,我...

Generate CSRF PoC 伪造跨站请求漏洞利用产生

今天要介绍的一种骇客攻击手法「伪造跨站请求」, 英文Cross-site request forge...

冒险村15 - customize tooltips with data attribute

15 - customize tooltips with data attribute 虽然这...