Golang学习系列(4)-面向对象编程

本文主要讲解Golang的面向对象编程,从关键概念出发,讲解了结构体、method及接口等内容

几个关键概念

Go面向对象与其他语言不同之处在于它不支持继承,Go只支持聚合(或称组合)和嵌入。

type ColoredPoint struct {
    color.Color  //匿名字段(嵌入)
    x,y int //真名字段(聚合)
}

Golang避开了”类”、”对象”、”实例”,而采用了”类型” 和 “值”,其中自定义类型的值可以包含方法。由于没有继承,因此没有虚函数。Go对此的支持则是采用类型安全的鸭子类型。即参数可以被声明为一个具体类型,也可以是提供了具体满足该接口的方法的值。

继承的一个优点是:有些方法在基类中实现一次,子类即可方便使用。Golang提供了两种解决方案,一种是使用了嵌入(其他语言叫委托delegation);另一种方案是为每一种类型提供独立的方法,只是简单包装。

Golang面向对象的另一个不同是它的接口、值和方法都相互保持独立。接口用于声明方法签名,结构体用于声明聚合或者嵌入的值,而方法用于声明在自定义类型上的操作。


struct

1.格式:

type StructName struct {
    Property1 type1
    ...
}

示例
type Person struct {
    Id int
    Name string
    age int
}

声明使用:
(1)
var P Person
P.Id = 1
P.Name = "xialingsc"
P.age = 32 //这里注意,age为小写开头,某些场景下无法获取该属性

(2) P := Person(1,"xialingsc",32)
(3) P := Persion(Id:1,Name:"xialingsc",age:32)

2.struct匿名字段

上面关键概念中已提及匿名字段,下面是初始化方法

point := ColoredPoint{Color{Red:122,Green:245,Blue:231},x:10,y:20}

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

sturct 不仅能将struct作为匿名字段,自定义类型、内置类型都可以作为匿名字段,而且可以在相应的字段上进行函数操作。若内嵌struct中含有与外层相同字段,则最外层的优先访问。这样可以去重载通过匿名字段继承的一些字段。

3.代码示例

/**
*
*xialingsc
*时间:2015-3-09
*
**/

package structtest

import (
    "fmt"
 )

type Skills []string

//常规struct定义
type person struct {
   name  string
   age   int
   email string
   phone string
}

//匿名字段(嵌入字段)举例,实现字段的继承
type Student struct {
    person     //匿名字段,默认Student就包含了Human的所有字段
    speciality string
    int               //不仅仅是struct,所有内置类型和自定义类型都可以作为匿名字段
    Skills            //匿名字段,自定义的类型string slice
    phone      string //最外层优先访问,可以利用用着去重载通过匿名字段继承的一些字段
}

func TestStruct() {
    var P person
    P.name = "xialing"
    P.age = 33
    P.email = "xialingsc@gmail.com"
    P.phone = "1234567890"
    //另两种声明方式
    //  P := person{"xial", 33, "xialingsc@163.com", "1234"}
    //  P := person{name: "xialing", age: 33, email: "", phone: ""}
    fmt.Printf("The person's name is %s", P.name)
}

func TestOlderPerson() {
    var p1, p2 person
    p1 = person{"xial", 33, "xialingsc@163.com", "1234"}
    p2 = person{name: "zhuangw", age: 30, email: "", phone: ""}
    olderperson, diff := older(p1, p2)
    fmt.Printf("Of %s and %s,%s is older by %d years", p1.name, p2.name, olderperson.name, diff)
}

func older(p1, p2 person) (person, int) {
    if p1.age > p2.age {
        return p1, p1.age - p2.age
    }
    return p2, p2.age - p1.age
}

func TestNiMingStruct() {
    mark := Student{person{"haim", 2, "haim@163.com", "1987223"}, "baby", 0, []string{"English"}, "99999"}
    fmt.Println("His name is ", mark.name)
    fmt.Println("His age is ", mark.age)
    fmt.Println("His email is ", mark.email)
    fmt.Println("His work phone is ", mark.phone)
    fmt.Println("His home phone is ", mark.person.phone)
    mark.name = "xiaohaim"
    mark.age = 3
    mark.speciality = "children"
    fmt.Println("His name is ", mark.name)
    fmt.Println("His age is ", mark.age)
    fmt.Println("His specility is ", mark.speciality)
    //通过匿名字段访问和修改字段很有用
    mark.person = person{"xiaohaim-1", 4, "163@163.com", "1213"}
    mark.person.age = 5
    fmt.Println("His name is ", mark.name)
    fmt.Println("His age is ", mark.age)
    fmt.Println("His specility is ", mark.speciality)
    mark.int = 3
    fmt.Println("His student's no is ", mark.int)
    mark.Skills = append(mark.Skills, "Chinese", "Japan")
    fmt.Println("His skills is ", mark.Skills)
}

interface

接口示例代码:

/**
*接口测试
*interface是一组method的组合,通过interface来定义对象的一组行为
*如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口,无需像java一样通过implements关键字显示指定
*interface到底能存什么值?可以存储实现这个interface的任意类型的对象
*xialingsc
*时间:2015-3-13
 */

 package interfacetest

 import (
    "fmt"
 )

type Human struct {
    name  string
    age   int
    phone string
}

type Student struct {
    Human
    school string
    loan   float32
}

type Teacher struct {
    Human
    salary     float32
    department string
}

//在person上定义了一个method
func (h Human) SayHi() {
    fmt.Printf("Hi,I am %s you can call me on %s\n", h.name, h.phone)
}

func (h Human) Sing(lyrics string) {
    fmt.Println("La la,la la la,....", lyrics)
}

func (h Human) Guzzle(beerStein string) {
    fmt.Println("Guzzle Guzzle...", beerStein)
}

func (t Teacher) SayHi() {
    fmt.Printf("Hi, I am %s,I work at %s.Call me on %s\n", t.name, t.department, t.phone)
}

func (s Student) SayHi() {
    fmt.Printf("This is Student.Sayhi,My name is %s------", s.name)
}


func (s Student) BorrowMoney(amount float32) {
    s.loan += amount
}

func (t Teacher) SpendSalary(amount float32) {
    t.salary -= amount
}

//定义interface
type Men interface {
    SayHi()
    Sing(lyrics string)
    Guzzle(beerStein string)
}

type YoungChap interface {
    SayHi()
    Sing(Song string)
    BorrowMoney(amount float32)

}

type ElderlyGent interface {
    SayHi()
    Sing(song string)
    SpendSalary(amount float32)
}
//根据上面的代码可知
//1.interface 可以被任意的对象实现
//2.Men interface 被 Human、Student、Teacher实现
//3.一个对象也可以实现多个接口,Student即实现了Men、YoungChap接口;Teacher实现了Men、ElderlyGent接口
//4.任意类型都实现了空interface(即 interface{},0个method方法)

func Testinterface() {
    mark := Student{Human{"Mark", 20, "66860989"}, "buaa", 4000.00}
    tom := Student{Human{"Tome", 21, "65860981"}, "buaa", 4000.00}
    jack := Teacher{Human{"Jack", 27, "87654091"}, 1500.89, "technolgy"}
    zhangy := Teacher{Human{"Zhangy", 47, "88768091"}, 6500.00, "technolgy"}
    //定义Men类型的变量i
    var imen Men
    //imen存储Student
    imen = mark
    fmt.Println("\n")
    fmt.Printf("This is Mark,a Student:")
    imen.SayHi()
    imen.Sing("November rain")
    //imen存储Teacher
    imen = jack
    fmt.Printf("This is Jack,a Teacher")
    imen.SayHi()
    imen.Sing("Born to be wild")

    //定义了slice Men
    fmt.Println("print all from slice Men")
    menArray := make([]Men, 3)
    //这三个都是不同类型的元素,但他们都实现了interface同一个接口
    menArray[0] = mark
    menArray[1], menArray[2] = tom, zhang
    for _, elementValue := range menArray {
        elementValue.SayHi()
    }                                                                                                                                                                  
 }

空接口测试

/**
*空interface 不包含任何的method,所有类型都实现了空interface
*空interface 对于描述起不到任何作用,但在需要存储任意类型的数值时有用,它可以存储任意类型的数值,类似C语言的void*类型
*非常重要的一点:一个函数把interface{}作为参数,那么它可以接受任意类型的值作为参数,如果一个函数返回interface{},则可以返回任意类型的值
*xialingsc
*时间:2015-3-13
 */

 package interfacetest
 import "fmt"
 func TestEmptyInterface() {
    //定义a 为空接口
    var a interface{}
    var i int = 4
    s := "Hi"
    //a可以存储任意类型
    a = i
    fmt.Printf("Ha Ha ,My type is int,the value is %d\n", a)
    a = s
    fmt.Printf("Ha Ha ,My type is string,the value is %s\n", a)
}

interface函数参数测试

/**
*interface的变量可以持有任意实现该interface类型的对象
*(1)通过定义interface参数,让函数接受各种类型的参数,例如fmt.Println方法,任何实现了String()方法的都能作为参数被fmt.Println调用
* 如果某个类型需要按特殊的格式输出,就必须实现这个Stringer接口
*(2)interface变量存储类型查看
*
*xialingsc
*时间:2015-3-13
 */

 package interfacetest

 import (
    "fmt"
    "strconv"
 )

type PHuman struct {
    name  string
    age   int
    phone string
}

//通过这个方法Human实现了Fmt.Stringer接口
//不实现方法,打印出来为This Human is: {Bob 39 00-86-109121}
//实现了,打印出来为This Human is: name:Bob-39years -00-86-109121---
func (h PHuman) String() string {
    return "name:" + h.name + "-" + strconv.Itoa(h.age) + "years -" + h.phone + "---"
}

func TestInterfaceParameter() {
    Bob := PHuman{"Bob", 39, "00-86-109121"}
    fmt.Println("This Human is:", Bob)
}

//-----------------------------------------
//(2)interface存储类型查看
//value,ok = element.(T),这个value就是变量的值,ok是一个bool类型,element是interface
//如果element里面存储了T类型的值,ok为true,否则ok为false
type Element interface{}
type List []Element

func TestInterfaceValueType() {
    list := make(List, 3)
    list[0] = 1       //an int
    list[1] = "hello" //a string
    list[2] = PHuman{"face", 10, "7654321"}
    for index, element := range list {
    //if value,ok := element.(int);ok{//comma-ok形式,功能同下
        switch value := element.(type) { //element.(type)不能在switch之外使用,在switch之外使用,必须使用comma-ok
            case int:
                fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
             case string:
                fmt.Printf("list[%d] is an string and its value is %s\n", index, value)
            case PHuman:
                fmt.Printf("list[%d] is an PHuman and its value is %s\n", index, value)
            default:
                fmt.Printf("list[%d] is of a different type", index)
        }
    }
}

嵌入式interface测试

/**
*嵌入式interface表示为如果一个interface1作为interface2的一个嵌入字段,
*那么interface2隐式的包含了interface1里面的method
*
*xialingsc
*时间:2015-3-13
*
 */

 package interfacetest

 import (
    "fmt"
    "io"
    "sort"
 )

type Interface interface {
    sort.Interface      //嵌入字段sort.Interface,把sort.Interface的所有method{即Len(),Less(i,j int),Swap(i,j int)}都包含进来
    Push(x interface{}) // a Push method to push elements into the heap
    Pop() interface{}   //a Pop elements that pops elements from the heap
}

type ReadWriter interface {
    io.Reader
    io.Writer
}

type Ceshi struct {
    lenght int
}

func (c Ceshi) Len() int {
    fmt.Println("The len of Ceshi")
    return 0
}

func (c Ceshi) Less(i, j int) bool {
    if i > j {
        return true
    }
    return false
}

func (c Ceshi) Swap(i, j int) {
}

func (c Ceshi) Push(x interface{}) {
}

func (c Ceshi) Pop() interface{} {
    return 4
}

func TestEmbededInterface() {
    var i Interface
    var c Ceshi = Ceshi{10}
    i = c
    fmt.Printf("This len of Interface:%d\n", i.Len())
}