【轻知识】Go入门学习整理——第三节(函数、struct、封装、继承、多态)

函数

Go只有值传递
Go函数返回值的两种写法。

这个其实没太多要说的。有的教程提到了面向函数范式编程(functional programming )。这篇文章中不说。

先说个点main函数这个函数不能有返回值跟参数。同样init函数也是如果你写了返回值。就会报红(must have no arguments and no return values)。当然即使不知道这点,也不会想到给main函数来点参数跟返回值哈。这也是main函数跟其他语言的main函数的不同之处。

定义一个除法函数。

func div(a int, b int) (int, error) {
    if b == 0 {
        return -1, errors.New(" b can't is zero value")
    }
    return a / b, nil
}

我们也可以写成如下方式。这种方式叫做 命名的返回值(named return variables)

func div(a int, b int) (result int, err error) {
    if b == 0 {
         result = -1
         err = errors.New(" b can't is zero value")
        return
    }
     result = a /b
     err = nil
    return 
}

我们在main中执行。

package main

import (
    "errors"
    . "fmt"
)
……(此处省略func div)
func main() {
    if result, error := div(10, 5); error == nil {
        Printf("a / b = %d\n", result)
    } else {
        Println(error)
    }
}

ok,函数一笔带过。闭包之后会看。

struct (模拟面向对象)

当你看到struct是不是想起了C。如下:

#include<stdio.h>

typedef struct student {
    char *name;
    int age;
} student, *stu;
int main() {
    student s1 = {"Mike", 11};
    printf("s1 name: %s, age: %d\n",s1.name, s1.age);
    stu s2=  &s1;
    printf("s2 name: %s, age: %d \n",s2->name, s2->age);
    return 1;
}

C中的指针访问是要用->(是不是想起了php中的$this->)。

关于struct 可以封装成员函数与方法。我能想到C++的做法。如下:

#include<iostream>
#include<string>
using namespace std;



struct Student {
        string name;
        int age;
        Student(string name, int age);
        void intro();
};
Student::Student(string n, int a) {
        name = n;
        age = a;
}
void Student::intro() {
        cout<<"name:"<<name<<" age:"<<age<<endl;
}
int main() {
        Student st1 = {"Mike", 11};
        st1.intro();
        Student* st2 = new Student("James", 12);
        st2->intro();
        return 0;
}

st1这种写法,在不同的编译环境可能是报错的。

$ g++ -std=c++0x student.cpp -o  stu
$ ./stu
name:Mike age:11
name:James age:12

OK,C++代码就不用去看了(大学学过C++可能之后就忘记了)。来看GO。我们还是写个student类。

type Student struct  {
    name string
    age int
}

func(st Student) intro() {
    Printf("name: %s, age: %d\n", st.name, st.age)
}

func main() {
    var st Student = Student{"Mike", 11}
    st.intro()
    var st1 Student = Student{age: 12}
    st1.name = "James"
    st1.intro()

    st2 := new(Student)
    st2.name = "Prince"
    st2.age = 13
    st2.intro()
    Println(st, st1, st2)
}

new 初始化返回的是一个指针。也就是st2保存的是一个指针。通过最后一行的Println能看出来。于是,你发现了没,C跟C++ 的struct指针都是用->,但Go还是用.

{Mike 11} {James 12} &{Prince 13}

值接收者的方法与指针接收者的方法

假如说我增加一个方法

func (st *Student) setName(name string) {
    st.name = name
}
var st Student = Student{age: 11}
st.setName("Mike")
st.intro()
st2 := new(Student)
st2.setName("Akon")
st2.age = 13
st2.intro()

输出

name: Mike, age: 11
name: Akon, age: 13

但如果是

func (st Student) setName(name string) {
    st.name = name
}

输出

name: , age: 11
name: , age: 13

所以,指针接收者的方法,用于改变对象状态的情况(你看到,即便s2是指针,但是setName接收者为值接收者也不会改变name值)。另外,指针的好处也在于拷贝一个指针,而不是拷贝一个对象(增大开销)。通常建议使用指针方法。

封装、继承、多态

面向对象的三大特性哈。

封装

说到封装。上面的student的例子就是封装了。但还缺点什么?那就是权限控制。之前我们了解的面向对象的语言中。会了解到public、protected、private(php都还好,C++中最烦,还有公有继承什么鬼的)。那Go中,这三个统统都没有哈。简单也粗暴。大写字母开头就是Public,那小写字母开头的就可以理解为protected了。

怎么理解呢?当我再创建一个文件。

package animal

type Animal struct {
    name string
    Age  int
}

然后引用这个animal。

func main() {
    var a = Animal{Age:11}
    Println(a.Age)
}

这样访问是可以的。如果Animal是小写的。那么引用的那个文件就会报红。同样如果Age属性或者Animal相关的方法都是小写开头。同样会报红。也就意味着。小写的方式它的作用域仅限于本包的作用域。

“继承”

通过嵌套组合模拟继承。

type Animal struct {
    name string
    age  int
}
func (a Animal) intro() {
    Printf("name: %s, age: %d\n", a.name, a.age)
}
type Bird struct {
    Animal
    category string
}
func (b Bird) intro() {
    Printf("name: %s, age: %d, category: %s\n", b.name, b.age, b.category)
}
func main() {
    var b Bird = Bird{Animal{"小鸟", 1} ,"文鸟科"}
    b.intro()
}

通常想到“继承”,会想到多态(下面说)、重载、子方法里面调用父类方法、

OK,重载是不支持的(当然php中也没有)。你可以把bird方法intro里面传一个参数。然后,b.intro()不变。就发现intro()并不会调用父类animal的无参intro()。

并且不能这样写var a Animal = Bird{Animal{"小鸟", 1} ,"文鸟科"}。对吧,也就是无法通过赋给父类实现多态。同样也不能实现父类访问子类的属性。不像某些语言。就拿php(java、c++也都如此)来说。看下面的代码体会:

<?php

class Animal {
    public $name;
    public $age;
    public function __construct(string $name, int $age) {
        $this->name = $name;
        $this->age = $age;
    }
    public function intro() {
        echo "name: {$name}, age: {$age}\n";
    }
    public function getCategory() {
        return $this->category;
    }
}

class Bird extends Animal {
    public $category;
    public function __construct(string $name, int $age, string $category) {
        parent::__construct($name, $age);
        $this->category = $category;
    }
    public function intro() {
        echo "name: {$name}, age: {$age}, category: {$category}\n";
    }
}

class Human extends Animal {
    public $category;
    public function __construct(string $name, int $age, string $category) {
        parent::__construct($name, $age);
        $this->category = $category;
    }
    public function intro() {
        echo "name: {$name}, age: {$age}, category: {$category}\n";
    }
}

function show(Animal $a) {
    echo '<pre>';
    var_dump($a);
    var_dump($a->getCategory());
}

show(new Bird('麻雀', 1, '文鸟科'));
show(new Human('智人', 1, '人科'));

所以,Go通过嵌套组合的“继承”不是传统意义上的继承。

“多态”

通过实现接口,模拟多态。先看PHP怎么做。我声明了一个接口Behaviour (当然这个例子不是很好,eat功能是animal必备的)。

<?php

interface Behaviour {
    public function eat();
}
class Animal {
    public $name;
    public $age;
    public function __construct(string $name, int $age) {
        $this->name = $name;
        $this->age = $age;
    }
    public function intro() {
        echo "name: {$name}, age: {$age}\n";
    }
    public function getCategory() {
        return $this->category;
    }
}

class Bird extends Animal implements Behaviour {
    public $category;
    public function __construct(string $name, int $age, string $category) {
        parent::__construct($name, $age);
        $this->category = $category;
    }
    public function intro() {
        echo "name: {$name}, age: {$age}, category: {$category}\n";
    }
    public function eat() {
        echo "eat worms";
    }
}

class Human extends Animal implements Behaviour {
    public $category;
    public function __construct(string $name, int $age, string $category) {
        parent::__construct($name, $age);
        $this->category = $category;
    }
    public function intro() {
        echo "name: {$name}, age: {$age}, category: {$category}\n";
    }
    public function eat() {
        echo "eat everything";
    }
}

function show(Behaviour $a) {
    echo '<pre>';
    var_dump($a);
    var_dump($a->getCategory());
    $a->eat();
}

show(new Bird('麻雀', 1, '文鸟科'));
show(new Human('智人', 1, '人科'));

那么,Go呢?既然不能通过赋给父类实现多态,也只能通过interface的这种形式了。

type Behaviour interface {
    eat()
}
type Animal struct {
    name string
    age  int
}

func (a Animal) intro() {
    Printf("name: %s, age: %d\n", a.name, a.age)
}

type Bird struct {
    Animal
    category string
}

func (b Bird) intro() {
    b.Animal.intro()
    Printf("name: %s, age: %d, category: %s\n", b.name, b.age, b.category)

}
func (b Bird) eat() {
    Println("eat worms")
}

type Human struct {
    Animal
    category string
}

func (h Human) intro() {
    Printf("name: %s, age: %d, category: %s\n", h.name, h.age, h.category)

}
func (b Human) eat() {
    Println("eat everything")
}

func show(b Behaviour) {
    b.eat()
}
func main() {
    var b Behaviour
    b = Bird{Animal{"麻雀", 1}, "文鸟科"}
    show(b)
    b = Human{Animal{"智人", 1}, "人科"}
    show(b)
    b = new(Bird)
    show(b)
    b = new(Human)
    show(b)
}

可以看出多态的实现,其实就是我实现了接口的方法就算实现了。而不是需要一个关键字“implements”。(这种实现方式也叫做duck type)

带标签的struct

带标签的struct放到最后讲下咯。

写beego的时候,我发现有struct 的字段的标签总带有json。后来知道这是json序列化的时候用的。

看如下代码(摘自网络):

type Product struct {
    Name      string 
    ProductID int64 
    Number    int   
    Price     float64  
    IsOnSale  bool  
}

func main() {
    p := &Product{}
    p.Name = "Xiao mi 6"
    p.IsOnSale = true
    p.Number = 10000
    p.Price = 2499.00
    p.ProductID = 1
    data, _ := json.Marshal(p)
    Println(string(data))
}

打印:

{"Name":"Xiao mi 6","ProductID":1,"Number":10000,"Price":2499,"IsOnSale":true}

但是,我们经常curd拿的数据库的字符串不是大写开头的。很多都是name,product_id这种格式的。那么我给struct加上tag(标签)。简单的追下代码也大概猜到。通过reflect (反射)拿到tag。然后把json:后面的名字作为字段名。(这段代码可以在encode.go文件,用鼠标点入Marshal进去,typeFields方法中看到处理的过程)

type Product struct {
    Name      string `json:"user_id"`
    ProductID int64 `json:"product_id"`
    Number    int  `json:"number"`
    Price     float64 `json:"price"`
    IsOnSale  bool `json:"is_on_sale"`
}

打印

{"user_id":"Xiao mi 6","product_id":1,"number":10000,"price":2499,"is_on_sale":true}

在你不了解访问权限的时候,你可能会想为什么struct中不写成这种,product_id,不就不用tag了呢?注意:大写是public哈。不然小写之后就别人就无法访问了。

扩展了解:关于tag、匿名字段、字段名字冲突、多重继承等可以了解《The way to go》的相关章节。

参考资料:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,741评论 2 9
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • 重新系统学习下C++;但是还是少了好多知识点;socket;unix;stl;boost等; C++ 教程 | 菜...
    kakukeme阅读 19,840评论 0 50
  • 我们中国的教育都先选专业,在读书。这就会导致一个致命的bug。在进入这个专业前我们根本不了解这个专业,我们掌握了这...
    姜友瑶_1849阅读 590评论 0 0
  • 整体上来看,讲者必须信任听众。台上的讲者,应该牢记, 相信他们(听众作为一个整体)是有判断力的,相信他们是有品位的...
    博远_2163阅读 353评论 0 0