golang学习日记(面向对象)

结构体与继承

什么是结构体?
它是将多个任意类型的变量组合在一起的聚合数据类型,可以理解为Go语言的结构体structural和其他语言的class有相等的地位

//定义结构体
type 结构体名 struct {
属性名 属性类型
属性名 属性类型

}

若相邻的属性(字段)是相同类型,可以合并写在一起

规则一:当最后一个字段和结果不在同一行时,, 不可省略。
规则二:字段名要嘛全写,要嘛全不写,不能有的写,有的不写。
规则三:初始化结构体,并不一定要所有字段都赋值,未被赋值的字段,会自动赋值为其类型的零值。

使用组合函数的方式来定义结构体方法

package main

import "fmt"

// 定义一个名为Profile 的结构体
type Profile struct {
    name   string
    age    int
    gender string
    mother *Profile // 指针
    father *Profile // 指针
}

// 定义一个与 Profile 的绑定的方法
func (person Profile) FmtProfile() {
    fmt.Printf("名字:%s\n", person.name)
    fmt.Printf("年龄:%d\n", person.age)
    fmt.Printf("性别:%s\n", person.gender)
}

func main() {
    // 实例化
    myself := Profile{name: "小明", age: 24, gender: "male"}
    // 调用函数
    myself.FmtProfile()
}

其中FmtProfile 是方法名,而(person Profile) :表示将 FmtProfile 方法与 Profile 的实例绑定。我们把 Profile 称为方法的接收者,而 person 表示实例本身,在方法内可以使用 person.属性名 的方法来访问实例属性。

方法的参数传递方式
想要在方法内改变示例的属性时,必须时使用指针作为方法的接收者

package main

import "fmt"

// 声明一个 Profile 的结构体
type Profile struct {
    name   string
    age    int
    gender string
    mother *Profile // 指针
    father *Profile // 指针
}

// 重点在于这个星号: *
func (person *Profile) increase_age() {
    person.age += 1
}

func main() {
    myself := Profile{name: "小明", age: 24, gender: "male"}
    fmt.Printf("当前年龄:%d\n", myself.age)
    myself.increase_age()
    fmt.Printf("当前年龄:%d", myself.age)
}

在这两种情况下,直接使用指针作为方法的接收者
1、需要在方法内部改变结构体内容的时候
2、出于性能问题,当结构体过大的时候

Go语言本身并部支持继承,但可以使用组合的方法,实现类似继承的效果
把一个结构体嵌入另外一个结构体的方法,称之为组合

package main

import "fmt"

type company struct {
    companyName string
    companyAddr string
}

type staff struct {
    name string
    age int
    gender string
    position string
    company
}

func main()  {
    myCom := company{
        companyName: "Tencent",
        companyAddr: "深圳市南山区",
    }
    staffInfo := staff{
        name:     "小明",
        age:      28,
        gender:   "男",
        position: "云计算开发工程师",
        company: myCom,
    }

    fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.companyName)
    fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.company.companyName)
}

内部方法与外部方法
方法首字母大写,相当于public,其他包可以随意调用
方法首字母小写,相当于private,其他包无法访问

三种实例化

//第一种,正常实例化
func main() {
    xm := Profile{
        name: "小明",
        age: 18,
        gender: "male",
    }
}

//第二种:使用new
func main() {
    xm := new(Profile)
    // 等价于: var xm *Profile = new(Profile)
    fmt.Println(xm)
    // output: &{ 0 }

    xm.name = "iswbm"   // 或者 (*xm).name = "iswbm"
    xm.age = 18     //  或者 (*xm).age = 18
    xm.gender = "male" // 或者 (*xm).gender = "male"
    fmt.Println(xm)
    //output: &{iswbm 18 male}
}

//第三种:使用 &
func main() {
    var xm *Profile = &Profile{}
    fmt.Println(xm)
    // output: &{ 0 }

    xm.name = "iswbm"   // 或者 (*xm).name = "iswbm"
    xm.age = 18     //  或者 (*xm).age = 18
    xm.gender = "male" // 或者 (*xm).gender = "male"
    fmt.Println(xm)
     //output: &{iswbm 18 male}
}

接口与多态

package main

import (
  "fmt"
  "strconv"
)

type good interface {

  TotalPrice() int
  OrderInfo() string
}

type fruits struct {
  price int
  quantity int
  name string

}

func (f fruits) TotalPrice() int {
  return f.price * f.quantity
}

func (f fruits) OrderInfo() string {
  return "你要购买" + strconv.Itoa(f.quantity) + "个"+ f.name +",总价是" +strconv.Itoa(f.TotalPrice()) + "元"
}

type seafood struct {
  price int
  quantity int
  name string
}

func (s seafood) TotalPrice() int {
  return s.price * s.quantity
}

func (s seafood) OrderInfo() string {
  return "你要购买" + strconv.Itoa(s.quantity) + "斤" + s.name +",总价是" +strconv.Itoa(s.TotalPrice()) + "元"
}

func SettleAccounts(goods []good) int {
  var all_price = 0
  for _,good := range goods{
    fmt.Println(good.OrderInfo())
    all_price += good.TotalPrice()
  }
  return all_price
}
func main() {
  apple := fruits{
    price: 5,
    quantity: 3,
    name: "苹果",
  }

  fish := seafood{
    price: 30,
    quantity: 2,
    name: "石斑鱼",
  }
  all_price := SettleAccounts([]good{apple,fish})
  fmt.Println("你总共消费",all_price,"元")
}

先定义了good的接口,里面有两个方法(多态)TotalPrice与OrderInfo,只要实现其中一个方法,那么这个结构体就是一个商品
然后定义两个结构体,分别时水果fruits与海鲜seafood,他们分别实现了good接口的两个方法

这时候挑选两个商品(实例化),分别是苹果与石斑鱼,创建一个购物车(类型为goo的切片),来存放商品,方法SettleAccounts计算购物车里面的订单金额

结构体的Tag

字段上还可以额外再加一个属性,用反引号(Esc键下面的那个键)包含的字符串,称之为 Tag,也就是标签。

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    Addr string `json:"addr,omitempty"`
}
package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    Addr string `json:"addr,omitempty"`
}

func main() {
    p1 := Person{
        Name: "Jack",
        Age:  22,
    }

    data1, err := json.Marshal(p1)
    if err != nil {
        panic(err)
    }

    // p1 没有 Addr,就不会打印了
    fmt.Printf("%s\n", data1)

    // ================

    p2 := Person{
        Name: "Jack",
        Age:  22,
        Addr: "China",
    }

    data2, err := json.Marshal(p2)
    if err != nil {
        panic(err)
    }

    // p2 则会打印所有
    fmt.Printf("%s\n", data2)
}

运行结果
$ go run demo.go
{"name":"Jack","age":22}
{"name":"Jack","age":22,"addr":"China"}

由于 Person 结构体里的 Addr 字段有 omitempty 属性,因此 encoding/json 在将对象转化 json 字符串时,只要发现对象里的 Addr 为 false, 0, 空指针,空接口,空数组,空切片,空映射,空字符串中的一种,就会被忽略。

type Person struct {
    Name        string `label:"Name is: "`
    Age         int    `label:"Age is: "`
    Gender      string `label:"Gender is: " default:"unknown"`
}
func Print(obj interface{}) error {
    // 取 Value
    v := reflect.ValueOf(obj)

    // 解析字段
    for i := 0; i < v.NumField(); i++ {

        // 取tag
        field := v.Type().Field(i)
        tag := field.Tag

        // 解析label 和 default
        label := tag.Get("label")
        defaultValue := tag.Get("default")

        value := fmt.Sprintf("%v", v.Field(i))
        if value == "" {
            // 如果没有指定值,则用默认值替代
            value = defaultValue
        }

        fmt.Println(label + value)
    }

    return nil
}

输出
$ go run demo.go
Name is: MING
Age is: 29
Gender is: unknown

空结构体

跟正常结构体一样,可以接收方法函数,表象特征就是没有任何属性,是一个不占用空间的对象

type Lamp struct{}

func main() {
    lamp := Lamp{}
    fmt.Print(unsafe.Sizeof(lamp))
}
// output: 0

基于这个特性,在一些特殊的场合之下,可以用做占位符使用,合理的使用空结构体,会减小程序的内存占用空间。

比如在使用信道(channel)控制并发时,我们只是需要一个信号,但并不需要传递值,这个时候,也可以使用 struct{} 代替。

func main() {
    ch := make(chan struct{}, 1)
    go func() {
        <-ch
        // do something
    }()
    ch <- struct{}{}
    // ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值