zoukankan      html  css  js  c++  java
  • Go语言面向对象

    Go语言面向对象及方法

    Go不是面向对象语言,但是却可以借助结构体模拟面向对象的特点。结构体在Go语言中的地位等同其他语言中的class。
    GO中这样的设计极大地降低了耦合,包括后面所说的接口,基本都是非侵入式的。

    一、面向对象

    面向对象的基本特征是:继承、封装、多态

    Go中的结构体是一种组合式的结构,对于属性的继承可以使用匿名字段的方式。而封装不必多说,对于多态的支持稍后详述。

    结构体的匿名字段

    结构体中的字段如果没有名字,称为匿名字段。原理是字段名和类型一样的话可以省略不写。

    func main()  {
    
    	a := animal{"Dog", "3", "汪汪"}
    	fmt.Println(a)
    	fmt.Printf("匿名字段的%s",a.string)
    }
    
    type animal struct {
    	name string
    	age string
    	//匿名字段
    	string
    }
    
    
    ..........................................
    //{Dog 3 汪汪}
    //匿名字段的汪汪
    

    我们增加一个匿名字段,类型是一个结构体。也就是结构体嵌套了。之前说过访问嵌套的结构体的字段,可以使用

    .连续访问。如果嵌套的结构体是匿名的,那么可以直接访问其字段。

    func main()  {
    
    	a := animal{"Dog", "3", "汪汪",Dog{"旺财","female"}}
    	fmt.Println(a)
    	fmt.Printf("匿名字段的%s
    ",a.string)
    	fmt.Printf("匿名字段的%s
    ",a.Dog)
    	fmt.Printf("匿名字段的%s
    ",a.Dog.sex)
    	//省略匿名字段名
    	fmt.Printf("匿名字段的%s
    ",a.sex)
    	fmt.Printf("匿名字段的%s
    ",a.Dog.name)
    	//名字冲突,指向父结构体
    	fmt.Printf("匿名字段的%s
    ",a.name)
    
    }
    
    type animal struct {
    	name string
    	age string
    	//匿名字段
    	string
    	Dog
    }
    
    type Dog struct {
    	name string
    	sex string
    }
    
    
    {Dog 3 汪汪 {旺财 female}}
    匿名字段的汪汪
    匿名字段的{旺财 female}
    匿名字段的female
    匿名字段的female
    匿名字段的旺财
    匿名字段的Dog
    

    继承-(提升字段)

    上面的Dog匿名字段变成了提升字段,a.Dog.sex和a.sex都可以访问,这就是模拟的继承。

    再写一个Demo:

    func main()  {
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school:=School{"友谊小学","安徽省杭埠镇",t,s}
    
    	//提升字段
    	fmt.Printf("学校%s老师的年龄是%d
    ",school.Teacher.teaName,school.Teacher.teaAge)
    	fmt.Printf("学校%s老师的年龄是%d
    ",school.teaName,school.teaAge)
        
    
    	fmt.Printf("学校%s同学的年龄是%d
    ",school.Student.stuName,school.stuAge)
    	fmt.Printf("学校%s同学的性别是%s
    ",school.Student.stuName,school.gender)
    
    }
    
    type School struct {
    	name string
    	address string
    	Teacher
    	Student
    }
    
    type Teacher struct {
    	teaName string
    	teaAge int
    }
    
    type Student struct {
    	stuName string
    	stuAge int
    	gender string
    }
    
    
    学校张三老师的年龄是23
    学校张三老师的年龄是23
    学校小明同学的年龄是13
    学校小明同学的性别是male
    

    可见性

    如果结构体名,字段名等是以小写字母开头,那么代表着private,其他包不可访问,如果大写,代表着public,其他包可访问。

    二、方法

    结构体就像是类的一种简化形式,那么类的方法在哪里呢?在Go语言中有一个概念,它和方法有着同样的名字,并且大体上意思相同:Go 方法是作用在接收器(receiver)上的一个函数,接收器是某种类型的变量。因此方法是一种特殊类型的函数。

    方法需要有一个接收器,才可以被执行。

    方法定义

    方法的定义方式和函数基本相同,只是需要在前方加一个接收器

    func (t  receiver_type) method_name() [return] {
    	方法体...
    }
    

    以上面的Demo为例,增加一个方法,然后在主函数中执行它。

    func main()  {
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school:=School{"友谊小学","安徽省杭埠镇",t,s}
    
    	school.display()
    }
    
    //方法,接收器类型为School,无返回值
    func (s School) display()  {
    	fmt.Println("学校的信息:",s)
    }
    
    
    //学校的信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    

    我们可以看到Go语言的方法也是非侵入式的,没有显式的声明是哪个语言的方法,一切由接收器决定。

    多态

    我们再写一个func (t Teacher) display()方法,这是个同名方法,接收器是Teacher类型,同样可以执行。

    func main()  {
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school:=School{"友谊小学","安徽省杭埠镇",t,s}
    
    	school.display()
    	t.display()
    	
    }
    
    func (s School) display()  {
    	fmt.Println("学校的信息:",s)
    }
    func (t Teacher) display()  {
    	fmt.Println("学校的信息:",t)
    }
    
    
    //学校的信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    //学校的信息: {张三 23}
    

    我们把接收器为子类School的方法删除,只留下一个接收器为父类Teacher的方法,主函数不变。

    此时的子类会先寻找接收器为本类型的方法(称为方法重写),如果没有,会寻找接收器为父类的方法,并执行。

    由于接收器是父类,父类没有子类的部分字段,所以只会执行父类的内容。

    这也是多态的一种体现,在其他语言中称为重载

    func main()  {
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school:=School{"友谊小学","安徽省杭埠镇",t,s}
    	
        //子类调用父类方法
    	school.display()
    	t.display()
    
    }
    
    func (t Teacher) display()  {
    	fmt.Println("学校的信息:",t)
    }
    
    //学校的信息: {张三 23}
    //学校的信息: {张三 23}
    
    

    指针接收器与非指针接收器

    接收器根据接收器的类型可以分为指针接收器、非指针接收器。两种接收器在使用时会产生不同的效果。根据效果的不同,两种接收器会被用于不同性能和功能要求的代码中。

    如果写出以下两种方法的。接收器类型一个是指针,一个非指针。

    func (s School) display()  {
    	fmt.Println("学校的信息:",s)
    }
    
    func (s *School) display()  {
    	fmt.Println("学校的信息:",s)
    }
    
    

    首先这样写是错的,这并不是多态性,因为这就是一个方法。

    对于方法来说,接收器类型指针和非指针,都可以调用方法。两者可以互相转换。

    非指针--指针

    func main()  {
    	
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school:=School{"友谊小学","安徽省杭埠镇",t,s}
    
    	school.display()
    
    }
    
    func (s *School) display()  {
    	fmt.Println("学校的信息:",s)
    }
    
    //学校的信息: &{友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    
    

    指针--指针

    func main()  {
    	school:=new(School)
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school=&School{"友谊小学","安徽省杭埠镇",t,s}
    
    	school.display()
    
    }
    
    func (s *School) display()  {
    	fmt.Println("学校的信息:",s)
    }
    
    ////学校的信息: &{友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    
    

    指针--非指针

    func main()  {
    	school:=new(School)
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school=&School{"友谊小学","安徽省杭埠镇",t,s}
    
    	school.display()
    
    }
    
    func (s School) display()  {
    	fmt.Println("学校的信息:",s)
    }
    
    //学校的信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    
    

    指针接收器

    指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的 this 或者 self。

    由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。

    我们测试一下:增加一个变量a,对不同类型进行组合。

    控制变量:以下每次只对接收器、接收器类型中的一个进行改变。

    指针--非指针接收器

    func main()  {
    
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school:=&School{"友谊小学","安徽省杭埠镇",t,s}
    
    	a:=school
    
    	school.display()
    
    	fmt.Println(school.name)
    	fmt.Println(a.name)
    
    
    	fmt.Println("学校信息:",a)
    
    }
    
    func (s School) display()  {
    	s.name="爱心小学"
    	fmt.Println("学校的信息:",s)
    }
    
    学校的信息: {爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    友谊小学
    友谊小学
    学校信息: &{友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    
    
    

    指针--指针接收器

    func main()  {
    
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school:=&School{"友谊小学","安徽省杭埠镇",t,s}
    
    	a:=school
    
    	school.display()
    
    	fmt.Println(school.name)
    	fmt.Println(a.name)
    
    
    	fmt.Println("学校信息:",a)
    
    }
    
    func (s *School) display()  {
    	s.name="爱心小学"
    	fmt.Println("学校的信息:",s)
    }
    
    
    学校的信息: &{爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    爱心小学
    爱心小学
    学校信息: &{爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    
    
    
    

    非指针--非指针接收器

    func main()  {
    
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school:=School{"友谊小学","安徽省杭埠镇",t,s}
    
    	a:=school
    
    	school.display()
    
    	fmt.Println(school.name)
    	fmt.Println(a.name)
    
    
    	fmt.Println("学校信息:",a)
    
    }
    
    func (s *School) display()  {
    	s.name="爱心小学"
    	fmt.Println("学校的信息:",s)
    }
    
    学校的信息: {爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    友谊小学
    友谊小学
    学校信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    
    

    非指针--指针接收器

    func main()  {
    
    	t:=Teacher{"张三",23}
    	s:=Student{"小明",13,"male"}
    
    	school:=School{"友谊小学","安徽省杭埠镇",t,s}
    
    	a:=school
    
    	school.display()
    
    	fmt.Println(school.name)
    	fmt.Println(a.name)
    
    
    	fmt.Println("学校信息:",a)
    
    }
    
    func (s *School) display()  {
    	s.name="爱心小学"
    	fmt.Println("学校的信息:",s)
    }
    
    
    学校的信息: &{爱心小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    爱心小学
    友谊小学
    学校信息: {友谊小学 安徽省杭埠镇 {张三 23} {小明 13 male}}
    
    
    

    已经可以得出结论了,接收器类型指针和非指针之间在方法中可以互相转换。

    如果方法的接收器类型为指针,那么方法中改变的值,方法结束后仍然有效。

    如果方法的接收器类型为非指针,那么方法中改变的值,方法结束后无效。

    方法的调用者类型不会被方法接收器类型修改。

  • 相关阅读:
    nginx http和https共存
    jQuery prop方法替代attr方法
    idea内置tomcat中java代码热更新
    Linux磁盘空间查看、磁盘被未知资源耗尽
    Mysql order by与limit混用陷阱
    IIS 架构解析
    asp.net MVC 应用程序的生命周期
    Linq专题之var关键字
    ThoughtWorks 2016年第1期DNA活动总结
    对象的深拷贝--反射
  • 原文地址:https://www.cnblogs.com/cgl-dong/p/14034834.html
Copyright © 2011-2022 走看看