golang中接口的面向对象(二)–继承

在上一节  golang中接口的面向对象(一)–多态特征中,我们学习了galang中的多态,

参考https://blog.51cto.com/91235688/2499405

  • 继承

那么这一节中,我们继续用上一节中的例子,来学习继承。

我们修改上一节中的示例代码,如下所示:

type geometry interface {

    sayHi()                     //geometry 只有一个方法sayHi()

}

type rect struct {

    geometry                   //这里使用匿名字段,使得rect 继承了 geometry 类型

    len, wid float32

}

func (r rect) sayHi() {             //这里rect 作为继承者,重写了sayHi方法

   fmt.Println("i am a rect")

}

func (r rect) area() float32 {        //这里rect 作为geometry 的超集,增加了area方法

    return r.len * r.wid

}

func (r rect) perim() float32 {      //这里rect 作为geometry 的超集,增加了perim方法

    return 2 * (r.len + r.wid)

}

type circle struct {

    rect                                   //这里使用匿名字段,使得circle 继承了 rect 类型

    radius float32

}

func show(name string, param interface{}) {

    switch param.(type) {

        case circle:

            fmt.Printf("[c]area of %v is %v \n", name, param.(circle).area())

            fmt.Printf("[c]perim of %v is %v \n", name, param.(circle).perim())

        case rect:

            fmt.Printf("[r]area of %v is %v \n", name, param.(rect).area())

            fmt.Printf("[r]perim of %v is %v \n", name, param.(rect).perim())

        default:

            fmt.Println("wrong type!")

    }

}

func main() {

    rec := rect{

        len: 1,

        wid: 2,

    }

    show("rect", rec)

    cir := circle{

        radius: 1,

    }

    show("circle", cir)

}

输出:

i am a rect

[r]area of rect is 2 

[r]perim of rect is 6 

i am a rect              

[c]area of circle is 0 

[c]perim of circle is 0 

circle 继承了 rect 类型的属性以及方法,因为circle类型长宽属性均为0,所以area 、perim 计算结果都是0

如果我们在rect类型对sayHi方法重写之前,加上下面这段代码,大家考虑一下会出现什么结果?

func (r geometry) sayHi() {

    fmt.Println("i am a geometry")

}

编译器会报invalid receiver type geometry (geometry is an interface type)这样的错误,

说明interface 类型不能作为方法的接收者,此刻它内部的方法是并未实现的,

从上面的例子,我们可以看到rect类型继承了geometry 类型的sayHi方法,并在之后重写了该方法,并且rect还增加了两个新的方法area以及perim

而circle类型则继承了rect类型的所有属性以及方法(仅仅作为一个例子,其实circle是不可能存在长、宽属性的)。

我们来修改一下上面的代码,show函数main函数保持不变,

type geometry interface {

    sayHi() 

}

type rect struct {

    len, wid float32

}

func (r rect) sayHi() {

    fmt.Println("i am a rect")

}

func (r rect) area() float32 {

    return r.len * r.wid

}

func (r rect) perim() float32 {

    return 2 * (r.len + r.wid)

}

type circle struct {

    rect                                //这里circle 继承了 rect的属性len, wid 以及方法sayHi、area、perim

    radius float32

}

func (c circle) area() float32 {            //这里circle 重写了 area方法

    return math.Pi * c.radius * c.radius

}

func (c circle) perim() float32 {         //这里circle 重写了 perim方法

    return 2 * math.Pi * c.radius

}

注意:上述代码中circle 并没有重写sayHi方法。

输出如下:

i am a rect

[r]area of rect is 2 

[r]perim of rect is 6 

i am a rect

[c]area of circle is 3.1415927        // 重写了 area方法,计算结果改变

[c]perim of circle is 6.2831855     // 重写了 perim方法,计算结果改变

我们修改代码,show函数main函数保持不变,为circle 增加对sayHi方法的重写,

type circle struct {

    rect

    radius float32

}

func (r circle) sayHi() {                    //这里circle 重写了 sayHi方法

    fmt.Println("i am a circle")

}

func (c circle) area() float32 {

    return math.Pi * c.radius * c.radius

}

func (c circle) perim() float32 {

    return 2 * math.Pi * c.radius

}

输出:

i am a rect

[r]area of rect is 2 

[r]perim of rect is 6 

i am a circle                   // 重写了 sayHi方法,打印发生改变

[c]area of circle is 3.1415927 

[c]perim of circle is 6.2831855 

  • 类型的变换

我们再一次修改代码,只修改main函数,其它代码不变,

func main() {

    var ige geometry    //接口类型

    var irec rect

    irec = rect{12}

    var icir circle

    icir.len = 0               //匿名字段的访问,有一点特别,以后的文章中我们再来学习

    icir.wid = 0              //匿名字段的访问,有一点特别

    icir.radius = 1

    fmt.Printf("ige addr is %p\n"&ige)  

    fmt.Printf("irec addr is %p\n"&irec)

    fmt.Printf("icir addr is %p\n"&icir)

   

    ige.sayHi()          //报错,ige是一个接口类型,其本身并未实现sayHi方法

    ige = irec           // rect转换为geometry类型,看起来是不是怀疑是一个指针传递?稍后我们再看

    fmt.Printf("ige addr is %p\n"&ige)       

    ige.sayHi()

    //ige.len = 1      //报错ige.len undefined (type geometry has no field or method len)

    show("rect", ige)

    ige = icir            //circle转换为geometry 类型  

    fmt.Printf("ige addr is %p\n"&ige)

    ige.sayHi()

    //ige.wid= 4          //报错

    //ige.radius= 4      //报错 

    show("circle", ige)

    //irec = icir         //报错,无法进行类型转换

    //icir = irec         //报错,无法进行类型转换

}

输出:

ige addr is 0xc000088240

irec addr is 0xc0000a20b0

icir addr is 0xc0000a20c0

ige addr is 0xc000088240

i am a rect

[r]area of rect is 2 

[r]perim of rect is 6 

ige addr is c000088240

i am a circle

[c]area of circle is 3.1415927 

[c]perim of circle is 6.2831855 

从上面代码可以看出,对ige = icir 赋值的操作,是一个值传递。

我们可以看到从一个超集(rect类型)赋值到 子集(geometry 类型),并没有发生内存溢出,

看来这是得益于golang的底层设计。


发表评论

电子邮件地址不会被公开。 必填项已用*标注