Golang - 面对"对象"

Golang - 面对"对象"

1. 简介

  • go语言对于面向对象的设计非常简洁而优雅
  • 没有封装、继承、多态这些概念,但同样通过别的方式实现这些特性
  • 封装:通过方法实现
  • 继承:通过匿名字段实现
  • 多态:通过接口实现

2. 匿名字段

go支持只提供类型而不写字段名的方式,也就是匿名字段,也称为嵌入字段

//package 声明开头表示代码所属包
package main

import "fmt"

//定义人的结构体
type Person struct {
	name string
	sex  string
	age  int
}

//学生
type Student struct {
	//匿名字段
	//默认Student包含了Person所有字段
	Person
	id   int
	addr string
}

func main() {
	
	s2 := Student{Person:Person{"约汉","female",10},id:2}
	fmt.Println(s2)
}

//{{约汉 female 10} 2 }

同名字段的情况

//package 声明开头表示代码所属包
package main

import "fmt"

//定义人的结构体
type Person struct {
	name string
	sex  string
	age  int
}

//学生
type Student struct {
	//匿名字段
	//默认Student包含了Person所有字段
	Person
	id   int
	addr string
	//同名字段
	name string
}

func main() {
	var s Student
	//就近赋值
	s.name = "约汉"
	fmt.Println(s)
	//若给外面赋值
	s.Person.name = "接客"
	fmt.Println(s)
}

//{{  0} 0  约汉}
//{{接客  0} 0  约汉}

所有的内置类型和自定义类型都是可以作为匿名字段去使用

package main

import "fmt"

//定义人的结构体
type Person struct {
   name string
   sex  string
   age  int
}

//自定义类型
type mystr string

//学生
type Student struct {
   //匿名字段
   //默认Student包含了Person所有字段
   Person
   //内置
   int
   mystr
}

func main() {
   //初始化
   s1 := Student{Person{"约汉","male",18},1,"bj"}
   fmt.Println(s1)
   fmt.Println(s1.name)
}

指针类型匿名字段

//package 声明开头表示代码所属包
package main

import "fmt"

//定义人的结构体
type Person struct {
	name string
	sex  string
	age  int
}

//学生
type Student struct {
	//匿名字段
	//默认Student包含了Person所有字段
	*Person
	//内置
	id int
	addr string
}

func main() {
	s1 := Student{&Person{"约汉","male",18},1,"bj"}
	fmt.Println(s1)
	fmt.Println(s1.name)
}

//{0xc0420661e0 1 bj}
//约汉

3. 方法

4. 包和封装

5. 接口

  • 在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数
  • 这种带有接收者的函数,我们称为方法,本质上,一个方法则是一个和特殊类型关联的函数
  • 方法的语法如下
  • func (接收参数名 接收类型) 方法名(参数列表)(返回值)
  • 可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法
  • 接收类型可以是指针或非指针类型
  • 为类型添加方法(为基础类型添加方法和为结构体类型添加方法)
    • 基础类型

        //package 声明开头表示代码所属包
        package main
        
        import "fmt"
        
        //任务:定义方法实现2个数相加
        
        type MyInt int
        
        //传统定义方式,面向过程
        func Add(a, b MyInt) MyInt {
        	return a + b
        }
        
        //面向对象
        func (a MyInt) Add(b MyInt) MyInt {
        	return a + b
        }
        
        func main() {
        	var a MyInt = 1
        	var b MyInt = 1
        	fmt.Println("Add(a,b)=", Add(a, b))
        	//调用面向对象的方法
        	fmt.Println("a.Add(b)=",a.Add(b))
        }
        
        
        //Add(a,b)= 2
        //a.Add(b)= 2
      
    • 结构体类型

        //package 声明开头表示代码所属包
        package main
        
        import "fmt"
        
        type Person struct {
        	name string
        	sex  string
        	age  int
        }
        
        //为Person添加方法
        func (p Person) PrintInfo() {
        	fmt.Println(p.name, p.sex, p.age)
        }
        
        func main() {
        	p := Person{"接客", "male", 18}
        	p.PrintInfo()
        }
      
    • 值语义和引用语义

        //package 声明开头表示代码所属包
        package main
        
        import "fmt"
        
        type Person struct {
        	name string
        	sex  string
        	age  int
        }
        
        //设置指针作为接收者的方法,引用语义
        func (p *Person) SetInfoPointer() {
        	(*p).name = "接客"
        	p.sex = "female"
        	p.age = 22
        }
        
        //值作为接收者,值语义
        func (p Person) SetInfoValue() {
        	p.name = "约汉"
        	p.sex = "male"
        	p.age = 20
        }
        
        func main() {
        	//指针作为接收者的效果
        	p1 := Person{"撸死", "male", 19}
        	fmt.Println("函数调用前=", p1)
        	(&p1).SetInfoPointer()
        	fmt.Println("函数调用后=", p1)
        	fmt.Println("================寂寞的分割线=================")
        
        	//值作为接收者的效果
        	p2 := Person{"约汉", "male", 18}
        	fmt.Println("函数调用前=", p2)
        	p2.SetInfoValue()
        	fmt.Println("函数调用后=", p2)
        }
        
        
        //函数调用前= {撸死 male 19}
        //函数调用后= {接客 female 22}
        //================寂寞的分割线=================
        //函数调用前= {约汉 male 18}
        //函数调用后= {约汉 male 18}
      
    • 方法的继承

        package main
        
        import "fmt"
        
        type Person struct {
           name string
           sex  string
           age  int
        }
        
        //为Person定义方法
        func (p *Person) PrintInfo()  {
           fmt.Printf("%s,%s,%d\n",p.name,p.sex,p.age)
        
        }
        
        type Student struct {
           Person
           id int
           addr string
        }
        
        func main()  {
           p :=Person{"接客","male",18}
           p.PrintInfo()
           //学生也去调,方法继承
           s := Student{Person{"接客","male",18},2,"bj"}
           s.PrintInfo()
        }
      
    • 方法的重写

        package main
        
        import "fmt"
        
        type Person struct {
           name string
           sex  string
           age  int
        }
        
        //为Person定义方法
        func (p *Person) PrintInfo() {
           fmt.Printf("%s,%s,%d\n", p.name, p.sex, p.age)
        }
        
        type Student struct {
           Person
           id   int
           addr string
        }
        
        //Student定义方法,实际上就相当于方法重写
        func (s *Student) PrintInfo() {
           fmt.Printf("Student:%s,%s,%d\n", s.name, s.sex, s.age)
        }
        
        func main() {
           p := Person{"接客", "male", 18}
           p.PrintInfo()
           //学生也去调,方法继承
           s := Student{Person{"接客", "male", 18}, 2, "bj"}
           s.PrintInfo()
           //显式调用
           s.Person.PrintInfo()
        }
      
    • 方法值和方法表达式

        package main
        
        import "fmt"
        
        type Person struct {
           name string
           sex  string
           age  int
        }
        
        func (p *Person) PrintInfoPointer() {
           //%p是地址,%v是值
           fmt.Printf("%p,%v\n", p, p)
        }
        
        func main() {
           p := Person{"接客", "male", 18}
           //传统的调用方法的方式
           p.PrintInfoPointer()
           //使用go方法值特性调用
           pFunc1 := p.PrintInfoPointer
           pFunc1()
           //使用go方法表达式调用
           pFunc2 := (*Person).PrintInfoPointer
           pFunc2(&p)
        }
      

练习:创建属性的getter和setter方法并进行调用

	package main
	
	import "fmt"
	
	type Dog struct {
	   name string
	   //1公  0母
	   sex int
	}
	
	//封装dog的方法
	//setter
	func (d *Dog) SetName(name string) {
	   d.name = name
	}
	
	//getter
	func (d *Dog) GetName() string {
	   return d.name
	}
	
	//咬人
	func (d *Dog) bite() {
	   fmt.Printf("让本汪%s 来给你上课...", d.name)
	}
	
	func main() {
	   d := Dog{"二哈", 1}
	   d.bite()
	}

4. 包和封装

  • 方法首字母大写:public
  • 方法首字母小写:private
  • 为结构体定义的方法必须放在同一个包内,可以是不同的文件
  • 上面代码复制到test包中,在test02包中进行调用,需要调用的方法名首字母大写

5. 接口

  • go语言中,接口(interface)是一个自定义类型,描述了一系列方法的集合

  • 接口不能被实例化

  • 接口定义语法如下

  • type 接口名 interface{}

  • PS:接口命名习惯以er结尾

  • 接口定义与实现

      package main
      
      import "fmt"
      
      //定义人的接口
      type Humaner interface {
         //说话
         Say()
      }
      
      //学生结构体
      type Student struct {
         name  string
         score int
      }
      
      //Student实现Say()方法
      func (s *Student) Say() {
         fmt.Printf("Student[%s,%d] 瞌睡不断\n", s.name, s.score)
      }
      
      type Teacher struct {
         name  string
         group string
      }
      
      //老师实现接口
      func (t *Teacher) Say() {
         fmt.Printf("Teacher[%s,%s] 毁人不倦\n", t.name, t.group)
      }
      
      //自定义类型
      type MyStr string
      
      //自定义类型实现方法
      func (str MyStr) Say() {
         fmt.Printf("MyStr[%s] 同志醒醒,还有个bug\n", str)
      }
      
      func WhoSay(i Humaner) {
         i.Say()
      }
      
      func main() {
         s := &Student{"约汉", 88}
         t := &Teacher{"撸死", "Go语言"}
         var tmp MyStr = "接客"
      
         s.Say()
         t.Say()
         tmp.Say()
      
         //go的多态,调用同一个接口,不同表现
         WhoSay(s)
         WhoSay(t)
         WhoSay(tmp)
      
         //make()创建
         x := make([]Humaner, 3)
         x[0], x[1], x[2] = s, t, tmp
         for _, value := range x {
            value.Say()
         }
      }
    
    • 接口继承

        package main
        
        import "fmt"
        
        //定义人的接口
        type Humaner interface {
           //说话
           Say()
        }
        type Personer interface {
           //等价于写了Say()
           Humaner
           Sing(lyrics string)
        }
        
        //学生结构体
        type Student struct {
           name  string
           score int
        }
        
        //Student实现Say()方法
        func (s *Student) Say() {
           fmt.Printf("Student[%s,%d] 瞌睡不断\n", s.name, s.score)
        }
        
        func (s *Student) Sing(lyrics string) {
           fmt.Printf("Student sing[%s]!!\n", lyrics)
        }
        
        func main() {
           s := &Student{"约汉", 88}
           var p Personer
           p = s
           p.Say()
           p.Sing("互撸娃")
        }
      
    • 空接口:空interface{}不包含任何方法,空接客可以存储任意类型的值

    • 类型查询

      • comma-ok断言

          package main
          
          import "fmt"
          
          //空接口
          type Element interface{}
          
          type Person struct {
             name string
             age  int
          }
          
          func main() {
             //切片
             list := make([]Element, 3)
             //int
             list[0] = 1
             list[1] = "Hello"
             list[2] = Person{"luhan", 18}
             //遍历
             for index, element := range list {
                //类型断言:value ,ok = element.(T)
                //value 是变量的值,ok是返回的布尔值,element是接口变量,T是断言类型
                if value, ok := element.(int); ok {
                   fmt.Printf("list[%d]是int类型,值是%d\n", index, value)
                } else if value, ok := element.(int); ok {
                   fmt.Printf("list[%d]是int类型,值是%d\n", index, value)
                } else if value, ok := element.(Person); ok {
                   fmt.Printf("list[%d]是Person类型,值是[%s,%d]\n",
                      index, value.name, value.age)
                } else {
                   fmt.Printf("list[%d]是其他类型\n", index)
                }
             }
          }
        
      • switch测试

        package main
        
        import "fmt"
        
        //空接口
        type Element interface{}
        
        type Person struct {
           name string
           age  int
        }
        
        func main() {
           //切片
           list := make([]Element, 3)
           //int
           list[0] = 1
           list[1] = "Hello"
           list[2] = Person{"luhan", 18}
           //遍历
           for index, element := range list {
              switch value := element.(type) {
              case int:
                 fmt.Printf("list[%d]是int类型,值是%d\n", index, value)
              case string:
                 fmt.Printf("list[%d]是string类型,值是%s\n", index, value)
              default:
                 fmt.Printf("list[%d]是其他类型\n", index)
              }
           }
        }
        
posted @ 2019-04-13 23:43  孔辉  阅读(260)  评论(0编辑  收藏  举报