面向对象

Go语言实现面向对象非常简单,因为它省略了很多繁琐又没什么用的特性,但又不像C语言那样在设计之初就没考虑面向对象的问题导致实现的比较绕。这篇笔记我们简单介绍Go语言中面向对象的用法。

结构体和方法

之前结构体章节我们已经学习过,Go语言没有类,但是在结构体上能够定义方法,这里我们再复习一下。

package main

import (
    "math"
    "fmt"
)

type Vector2 struct {
    X float64
    Y float64
}

func (v *Vector2) Abs() float64 {
    return math.Sqrt(v.X * v.X + v.Y * v.Y)
}

func main() {
    v := &Vector2{1, 2}
    fmt.Println(v.Abs())
}

其中v就像一个参数,只不过它代表的是具有该方法的对象。至于为什么使用指针作为方法调用者参数,一般来说如果是复杂的自定义类型,是建议这么做的,这样可以防止对象拷贝的巨大开销。

接口和多态

下面例子定义了一个接口Vector,其中定义了能够实现计算向量长度的方法,Vector2Vector3实现了这个方法。

package main

import (
    "math"
    "fmt"
)

type Vector interface {
    Abs() float64
}

type Vector3 struct {
    X float64
    Y float64
    Z float64
}

type Vector2 struct {
    X float64
    Y float64
}

func (v *Vector2) Abs() float64 {
    return math.Sqrt(v.X * v.X + v.Y * v.Y)
}

func (v *Vector3) Abs() float64 {
    return math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z)
}


func main() {
    var v1 Vector
    var v2 Vector
    v1 = &Vector2{1, 2}
    v2 = &Vector3{1, 2, 3}
    fmt.Println(v1.Abs())
    fmt.Println(v2.Abs())
}

这里向v1v2赋值时,它们的类型我们声明为接口Vector,实际上它是指针,而且默认值是nil

我们初始化的是接口的实现类Vector2Vector3,这时必须加上取地址符,这和Java中不同,不要弄混了。如果声明v1v2后没有为其赋值,就会导致空指针。此外,如果向函数传递Vector类型的参数,它显然也是引用传递。

其实Go语言中,我们可以让编译器去进行自动类型推导:

v1 := &Vector2{1, 2}

这个类型推导是比较强大的,它会自动根据上下文决定变量的数据类型,因此我们就不用总是纠结应该定义为何种类型了。

实现fmt.Stringer.String()

Java中,所有对象的基类是Object,该类实现了toString()方法用于打印对象。Go语言中也有类似功能,但是设计的稍有不同。Go语言中,结构体可以实现Stringer接口的String()方法,实现自身打印输出,fmt会自动调用该方法格式化输出变量。

package main

import (
    "math"
    "fmt"
)

type Person struct {
    name string
    age int
}

func (p *Person) String() string {
    return fmt.Sprintf("%v / %v", p.name, p.age)
}

func main() {
    p := &Person{"Tom", 10}
    fmt.Println(p)
}

运行输出:

Tom / 10

糟糕的设计:隐式实现接口

看过上面代码,你应该已经注意到Go语言并没有使用implements显示的指定该结构体实现了某某接口,它完全是隐式实现的,只要结构体上定义的方法实现能够对应到某一接口上,它就被认为是实现了某一接口。

这个特性只能说是自作聪明,设计的比较傻了,严重降低了代码可读性。要想在阅读别人代码时,知道一个结构体实现了哪个接口,不依赖IDE就很困难了。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap