• Golang入门教程(十四)结构体和类详解


    golang中并没有明确的面向对象的说法,实在要扯上的话,可以将struct比作其它语言中的class。

    类声明

    type Book struct {
        Title  string
        Author string
        intro  string
    }
    

      这样就声明了一个类,其中没有public、protected、private的的声明。golang用另外一种做法来实现属性的访问权限:属性的开头字母是大写的则在其它包中可以被访问,否则只能在本包中访问。类的声明和方法亦是如此。

    类方法声明

    // 类方法声明-传递值对象
    func (b Book) B1() {
    	b.Title = "Book001"
    	b.Author = "ErWan"
    }
    
    // 类方法声明-传递指针对象
    func (b *Book) B2() {
    	b.Title = "Book002"
    	b.Author = "Tinywan"
    }
    

      和其它语言不一样,golang声明方法和普通方法一致,只是在func后增加了b Book这样的声明。加和没有加*的区别在于一个是传递指针对象(加*),一个是传递值对象。

    传递指针和对象的区别

    package main
    
    import "fmt"
    
    // 类声明
    type Book struct {
    	Title    string
    	Author   string
    	DateTime string
    }
    
    // 类方法声明-传递值对象
    func (b Book) B1() {
    	b.Title = "Book001"
    	b.Author = "ErWan"
    }
    
    // 类方法声明-传递指针对象
    func (b *Book) B2() {
    	b.Title = "Book002"
    	b.Author = "Tinywan"
    }
    
    func main() {
    	/*声明一个 Book 类型的变量 b ,并调用 B1() 和 B2()*/
    	b := Book{"Def-Book", "Def-Author", "Def-DateTime"}
    
    	fmt.Println("B1 调用前:", b.Title, b.Author, b.DateTime)
    	b.B1()
    	fmt.Println("B1 调用后:", b.Title)
    
    	fmt.Println("------------------
    ")
    	fmt.Println("B2 调用前:", b.Title)
    	b.B2()
    	fmt.Println("B2 调用后:", b.Title)
    
    }
    

     执行结果:

    B1() 的接收者是值类型 Book, B2() 的接收者是值类型 *Book , 两个方法内都是改变Name值。

    小结:

      1、接收者可以看作是函数的第一个参数,即这样的: func B1(b Book), func B2(b *Book)。 go不是面向对象的语言,所以用那种看起来像面向对象的语法来理解可能有偏差。

      2、当调用 b.B1() 时相当于 B1(b) ,实参和行参都是类型 Book,可以接受。此时在B1()中的b只是 "Def-Book" 的值拷贝,所以B1()的修改影响不到" Def-Book"。

      3、当调用 b.B2() => B2(b1),这是将 Book 类型传给了 *Book 类型,go可能会取 b 的地址传进去: B2(&b)。所以 B2() 的修改可以影响 b 。

     

     例如在一个beego 框架中我们要修改一个action的值,是这么定义的(为了修改内部结构的值。而不是传递一下)

    func (this *InputController) InputGet(){
         // Get 方式接受 name := this.GetString("name") // 不使用模版,直接用 this.Ctx.WriteString 输出字符串 this.Ctx.WriteString(name) }

    值类型不可以改变值,而指针类型则是可以的

    匿名结构体

    p := struct {
        Name string
        Gender string
        Age uint8
    }{"Robert", "Male", 33}

    匿名结构体最大的用处是在内部临时创建一个结构以封装数据,而不必正式为其声明相关规则。 

    实例化对象

    实例化对象有好几种方式:

    package main
    
    // 类声明
    type Person struct {
    	Name    string
    	Age   	int
    	Doc		[]string // slice切片
    }
    
    // 类方法声明-传递值对象
    func (p *Person) P1() {
    	p.Name = "Tinywan"
    	p.Age = 24
    }
    
    // 类方法声明-传递指针对象
    func (p *Person) P2() {
    	p.Name = "Tinyaiai"
    	p.Age = 22
    }
    
    func main() {
    
    	// 实例化对象 实例化的时候可以初始化属性值,如果没有指明则默认为系统默认值
    	p1 := &Person{}
    	p1.Name = "ShaoBo Wan"
    	p1.Age = 20
    
    	p2 := &Person{Name:"HaHa"}
    
    	p3 := new(Person)
    	p3.Name = "New Name"
    
    	p4 := Person{}
    	p4.Name = "Name 001"
    	p4.Age = 26
    
    	p5 := Person{Name:"Name 002",Age:28}
    
    	// 使用中如果包含数组(动态数组 slice切片),结构体的实例化需要加上类型如上如果intro的类型是[]string
    	p6 := &Person{
            "zhangsan",
            25,
            []string{"lisi", "wangwu"},
        }
    }

    注意,最后一个实例化

        p6 := &Person{
            "zhangsan",
            25,
            []string{"lisi", "wangwu"},
        }
    

    小结:

      1、使用中如果包含数组,结构体的实例化需要加上类型如上如果Doc的类型是[]string。  

      2、实例化的时候可以初始化属性值,如果没有指明则默认为系统默认值。

      3、加&符号和new的是指针对象,没有的则是值对象,这点和php、java不一致,在传递对象的时候要根据实际情况来决定是要传递指针还是值

      4、当对象比较小的时候传递指针并不划算。

     继承

    确切的说golang中叫做组合(composition)

    1、先初始化为空再赋值

    // 先初始化为空再赋值
    s1 := &Student{}
    s1.schoole = "QiHua"
    

    2、直接赋值

    // 直接赋值
    s2 := &Student{
    	Persons: Persons{
    		Name:"Tinywan",
    		Age:24,
    		Doc:[]string{"H1","h2"},
    	},
    	schoole:"BeiJin Schoole",
    }
    

    3、完整代码

    package main
    
    import (
    	"fmt"
    )
    
    // 类声明
    type Persons struct {
    	Name    string
    	Age   	int
    	Doc		[]string // slice切片
    }
    
    // 获取Name
    func (p *Persons) getName() {
    	fmt.Println("Name is ",p.Name)
    }
    
    type Student struct {
    	// Student 属性中声明了 Persons,表示组合了Persons 的属性和方法(属性和方法都会被继承)
    	Persons
    	Name string
    	schoole string
    }
    
    func main() {
    
    	// 先初始化为空再赋值
    	s1 := &Student{}
    	// 当访问Name的时候默认为ProsePoem的 Name,如果需要访问Persons 的 Name 属性可以使用 s1.Persons.Name 来访问方法同理。
    	s1.Name = "Tinywan"
    	s1.Persons.Name = "Persons Name"
    	s1.schoole = "QiHua"
    	fmt.Println("s1 = ",s1)
    	fmt.Println("
    ")
    
    	// 直接赋值
    	s2 := &Student{
    		Persons: Persons{
    			Name:"Tinywan",
    			Age:24,
    			Doc:[]string{"s2-Doc","s2-Doc"},
    		},
    		Name:"ErWan",
    		schoole:"BeiJin-Schoole",
    	}
    	fmt.Println("s2 = ",s2)
    }
    

    方法的继承和属性一致

      

    参考

    1、https://segmentfault.com/a/1190000012325027

    2、 

     

  • 相关阅读:
    精通正则表达式(JavaScript)
    Go知识点记录
    多线程揭秘
    Python test
    ELinq+T4模版引擎制作多文件实体代码生成器
    浏览器内核
    MongoDb的增删改查
    LINQ执行表达式
    ASP.NET MVC3 读书笔记四(数据注解和验证)
    C#默认以管理员身份运行程序
  • 原文地址:https://www.cnblogs.com/tinywan/p/8573702.html
Copyright © 2020-2023  润新知