golang学习笔记之二 - 面向对象基础

2023-09-05 16:11:47 浏览数 (1)

学习笔记 golang

面向对象基础

  • struct基础:自定义类型,Go支持只提供类型,而不写字段名的方式,也就是匿名字段,也称为嵌入字段。当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。
代码语言:javascript复制
// 定义一个 人类 struct
type person struct {
    name string
    age int
}

// 定义一个 学生 struct,继承了人类struct
type student struct {
    person    // 匿名字段
    class1 int
    tercherName string
}

func s1()  {
    var p1 person
    p1.age = 22
    p1.name = "John"
    fmt.Println(p1)

    p2 := person{name:"Jack", age:55}
    fmt.Println(p2)
}

func s2()  {
    var student = student{person{"小红", 18}, 8, "陈老师"}
    fmt.Println(student)
    student.tercherName = "老李"
    fmt.Println(student.name, student.person.name)
}

func main()  {
    s1()
    s2()
}
  • 属于struct的方法(类内的方法)

假如要计算面积,一般思路如下方法一所示:但是areaRectangle()不是作为Rectangle的方法实现的(类似面向对象里面的方法),而是将Rectangle的对象(如r1,r2)作为参数传入函数计算面积的,那么如果增加一个图形,想计算面积就只能增加新的函数,函数名也必须要跟着更换,变成area_rectangle, area_circle, area_triangle...。用面向对象的思想来说,面积应该是对象的一个方法Rectangle.area(),而非外围函数。

代码语言:javascript复制
type Rectangle struct {
    width, height float64
}

type Cricle struct {
    radius float64
}

func areaRectangle(r Rectangle) float64  {
    return r.height * r.width
}

//func areaCricle(c Cricle) float64  {
//    // 计算公式等等...
//}

func main()  {
    // 计算长方形面积方法一
    // 如果有多个不同的形状如圆形,就需再写一个外部方法来计算,由此引出方法二:写一个从属struct的方法
    var rec1 = Rectangle{4, 9}
    res1 := areaTest(rec1)
    fmt.Println(res1)
}

定义method语法:func (r ReceiverType) funcName(parameters) (results),则如上例子可以用下面method来实现

代码语言:javascript复制
type Rectangle struct {
    width, height float64
}

type Cricle struct {
    radius float64
}

func (r Rectangle) area() float64 {
    return r.width * r.height
}

func (c Cricle) area() float64  {
    return c.radius * c.radius * math.Pi
}

func main()  {
    var rectangle = Rectangle{3, 5}
    var cricle = Cricle{10}

    fmt.Println(rectangle.area())
    fmt.Println(cricle.area())
}

注意 :虽然method的名字一模一样,但是主体不一样,那么method就不一样;method里面可以访问接收者的字段;默认情况下方法的Receiver(即类主体)是值传递,而非引用,在method里改变字段是不影响原类的,method同样支持传递引用;调用method通过.访问,就像struct里面访问字段一样。

代码语言:javascript复制
func (r *Rectangle) area2() float64 {
    r.width = 10             // 问题:这里为什么没用 *r.width = 10 ?
    return r.width * r.height
}

// 先调用area2,发现rectangle已经被更改了,因为传递了引用
fmt.Println(rectangle.area2())
fmt.Println(rectangle)

问题解答: 如果一个method的receiver是T,你可以在一个T类型的实例变量V上面调用这个method,而不需要&V去调用这个method;如果一个method的receiver是T,你可以在一个T类型的变量P上面调用这个method,而不需要 *P去调用这个method;所以不用担心你是调用的指针的method还是不是指针的method,Go已经帮你搞定了。

method继承:如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method

代码语言:javascript复制
type Rectangle struct {
    width, height float64
}

type someStruct struct {
    Rectangle    // 继承rectangle,则可以调用所有方法
    params int
}

s1 := someStruct{Rectangle{5, 8}, 10}
fmt.Println(s1.area())    // 40
fmt.Println(s1)           // {{5 8} 10}

method 重写:

代码语言:javascript复制
type Rectangle struct {
    width, height float64
}

type someType struct {
    Rectangle
    deep float64
}

func (r Rectangle) area(x float64) (res float64)  {
    // 在函数定义了返回值就不需要在函数内重重新声明,直接return即可
    // var res float64
    if x > 0 {
        res = r.width * r.height * x
    } else {
        res = r.width * r.height
    }

    return
}

// 重写area方法
func (s someType) area(x float64) (res float64)  {
    if x > 0 {
        res = s.width   s.height   x
    } else {
        res = s.width   s.height
    }

    return
}

var someType = someType{Rectangle{2, 5}, 3}
fmt.Println(someType)                        // {{2 5} 3}
fmt.Println(someType.Rectangle.area(1))      // 10
fmt.Println(someType.area(1))                // 8

interface

interface可以理解为是一组method签名的组合,我们通过interface来定义对象的一组行为。

代码语言:javascript复制
// 学生接口 interface就是一组抽象方法的集合
type studentInterface interface {
    sayHi()
    sing()
    //changeSchool(schoolName string)
}

// Human 结构体
type Human struct {
    name string
    age int
    phone string
}

// Student结构体,继承Human
type Student struct {
    Human
    school string
    grade int
}

// Human 对象实现Sayhi方法
func (human Human) sayHi() {
    fmt.Printf("hi, i am %s, and i am %d, call me %s", human.name, human.age, human.phone)
}

// Human 对象实现Sing方法
func (human Human) sing() {
    fmt.Println("lalalalala")
}

// Human 对象实现Guzzle方法
func (human Human) Guzzle(beerStein string) {
    fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
}

// student 重载Human的sayHi方法
func (student Student) sayHi() {
    fmt.Println("hi im a student, my name is", student.name, "grade is", student.grade)
}

// student 重载Human的sing方法
//func (student Student) sing() {
//    fmt.Println("lala")
//}

// student 实现转学方法
func (student *Student) changeSchool(schoolName string) {
    student.school = schoolName
}

// 执行方法
func interfaceTest()  {
    var studentI studentInterface

    var Tom = Student{Human{"tom", 18, "13000000000"},"北京大学", 8}
    var John = Human{"shyzhen", 26, "14526555544"}

    // 对象必须完全实现了interface定义的方法才能赋值 可多不可少。如Student同时实现了changeSchool方法,接口中并未定义。
    studentI = Tom
    studentI.sayHi()
    studentI.sing()

    studentI = John
    studentI.sayHi()
    studentI.sing()

    Tom.sayHi()
    Tom.sing()
    Tom.changeSchool("清华大学")
    fmt.Println(Tom)
    John.sayHi()
    John.sing()
}

由于所有的类型都实现了空interface,一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。

代码语言:javascript复制
func interfaceTest2(params interface{}) interface{} {
    return params
}
fmt.Println(interfaceTest2("sssss"))
fmt.Println(interfaceTest2(2323))

0 人点赞