美文网首页工作生活
golang类和结构体

golang类和结构体

作者: 杰克慢 | 来源:发表于2019-07-01 14:57 被阅读0次

    golang结构体和类

    golang中并没有明确的面向对象的说法,实在要扯上的话,可以将struct比作其它语言中的class。

    类声明

    type Poem struct {
        Title  string
        Author string
        intro  string
    }
    

    这样就声明了一个类,其中没有public、protected、private的的声明。golang用另外一种做法来实现属性的访问权限:属性的开头字母是大写的则在其它包中可以被访问,否则只能在本包中访问。类的声明和方法亦是如此。

    类方法声明

    func (poem *Poem) publish() {
        fmt.Println("poem publish")
    }
    

    或者

    func (poem Poem) publish() {
        fmt.Println("poem publish")
    }
    

    和其它语言不一样,golang声明方法和普通方法一致,只是在func后增加了poem Poem这样的声明。加和没有加*的区别在于一个是传递指针对象,一个是传递值对象。

    传递指针和对象的区别

    type T struct {
        Name string
    }
    
    func (t T) M1() {
        t.Name = "name1"
    }
    
    func (t *T) M2() {
        t.Name = "name2"
    }
    

    M1() 的接收者是值类型 T, M2() 的接收者是值类型 *T , 两个方法内都是改变Name值。

    下面声明一个 T 类型的变量,并调用 M1() 和 M2() 。

        t1 := T{"t1"}
    
        fmt.Println("M1调用前:", t1.Name)
        t1.M1()
        fmt.Println("M1调用后:", t1.Name)
    
        fmt.Println("M2调用前:", t1.Name)
        t1.M2()
        fmt.Println("M2调用后:", t1.Name)
    

    输出结果为:

    M1调用前: t1
    M1调用后: t1
    M2调用前: t1
    M2调用后: name2

    下面猜测一下go会怎么处理。

    先来约定一下:接收者可以看作是函数的第一个参数,即这样的: func M1(t T), func M2(t *T)。 go不是面向对象的语言,所以用那种看起来像面向对象的语法来理解可能有偏差。

    当调用 t1.M1() 时相当于 M1(t1) ,实参和行参都是类型 T,可以接受。此时在M1()中的t只是t1的值拷贝,所以M1()的修改影响不到t1。

    当调用 t1.M2() => M2(t1),这是将 T 类型传给了 *T 类型,go可能会取 t1 的地址传进去: M2(&t1)。所以 M2() 的修改可以影响 t1 。

    匿名结构体

    p := struct {
        Name string
        Gender string
        Age uint8
    }{"Robert", "Male", 33}
    

    匿名结构体最大的用处是在内部临时创建一个结构以封装数据,而不必正式为其声明相关规则。

    实例化对象

    实例化对象有好几种方式

    poem := &Poem{}
    
    poem.Author = "Heine"
    
    poem2 := &Poem{Author: "Heine"}
    
    poem3 := new(Poem)
    
    poem3.Author = "Heine"
    
    poem4 := Poem{}
    
    poem4.Author = "Heine"
    
    poem5 := Poem{Author: "Heine"}
    

    实例化的时候可以初始化属性值,如果没有指明则默认为系统默认值
    注意:

    p1 := &Poem{
            "zhangsan",
            25,
            []string{"lisi", "wangwu"},
        }
    

    使用中如果包含数组,结构体的实例化需要加上类型如上如果intro的类型是[]string。

    加&符号和new的是指针对象,没有的则是值对象,这点和php、java不一致,在传递对象的时候要根据实际情况来决定是要传递指针还是值。

    tips:当对象比较小的时候传递指针并不划算。

    构造函数(自己创造)

    func NewPoem(param string, p ...interface{}) *Poem
    

    示例:

    func NewPoem(author string) (poem *Poem) {
        poem = &Poem{}
        poem.Author = author
        return
    }
    
    poem6 := NewPoem("Heine")
    

    继承

    确切的说golang中叫做组合(composition)

    func (e *Poem) ShowTitle() {
        fmt.Printf(e.Title)
    }
    type Poem struct {
        Title  string
        Author string
        intro  string
    }
    
    type ProsePoem struct {
        Poem
        Author string
    }
    

    ProsePoem属性中声明了Poem,表示组合了Poem的属性和方法(属性和方法都会被继承)

    初始化方式

    1、先初始化为空再赋值

    prosePoem := &ProsePoem{}
    prosePoem.author = "Heine"
    

    2、直接赋值

    prosePoem := &ProsePoem{
            Poem: Poem{
                Title:  "Jack",
                Author: "slow",
                intro:  "simple",
            },
            Author: "test",
        }
    

    如果其中属性有冲突,则以外围的为主,也就是说会被覆盖。

    type ProsePoem struct {
        Poem
        Author string
    }
    

    当访问Author的时候默认为ProsePoem的Author,如果需要访问Poem的Author属性可以使用
    prosePoem.Poem.Author来访问方法同理**。

    prosePoem := &ProsePoem{}
    
    prosePoem.Author = "Shelley"
    
    prosePoem.Poem.Author = "Heine"
    
    fmt.Println(prosePoem)
    

    从输出中可以很直观看到这一点。

    &{{ Heine } Shelley}

    方法的继承和属性一致,这里不再罗列。通过组合的话可以很好的实现多继承。

    多继承

    比如有一个父亲,是中国人:

    type Father struct {
        MingZi string
    }
    
    func (this *Father) Say() string {
        return "大家好,我叫 " + this.MingZi
    }
    

    可以理解为父亲类有一个属性,有一个Say方法

    有父亲当然有母亲,母亲是个外国人:

    type Mother struct {
        Name string
    }
    
    func (this *Mother) Say() string {
        return "Hello, my name is " + this.Name
    }
    

    父亲和母亲结合有了孩子类,孩子类继承了父亲和母亲:

    type Child struct {
        Father
        Mother
    }
    

    然后孩子类有一个实例c:

        c := new(Child)
        c.MingZi = "张小明"
        c.Name = "Tom Zhang"
    

    因为MingZi和Name这个属性在Mother和Father中并没有冲突,所以可以直接使用 c. 就可以获取而没有问题

    但是,如果这样直接调用Child类的Say方式:

    c.Say()
    

    会出现冲突:

    ambiguous selector c.Say
    

    怎么办?其实这样就可以轻松解决:

        c.Father.Say()
        c.Mother.Say()
    

    上面两条表达式的值分别为:

    大家好,我叫 张小明
    Hello, my name is Tom Zhang

    方法重载

    方法重载就是一个类中可以有相同的函数名称,但是它们的参数是不一致的,在java、C++中这种做法普遍存在。golang中如果尝试这么做会报重新声明(redeclared)错误,但是golang的函数可以声明不定参数,这个非常强大。

    func (poem *Poem) recite(v ...interface{}) {

    fmt.Println(v)
    

    }

    其中v …interface{}表示参数不定的意思,其中v是slice类型,fmt.Println方法也是这样定义的。如果要根据不同的参数实现不同的功能,要在方法内检测传递的参数。

    相关文章

      网友评论

        本文标题:golang类和结构体

        本文链接:https://www.haomeiwen.com/subject/ypbgcctx.html