[go学习笔记]十二、行为的定义和实现

144 阅读2分钟

Is Go an object-oriented language?

Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).

Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.

封装数据和行为

结构体定义

type Employee struct {
  Id string
  Name string
}

实例创建及初始化

e := Employee{"0","Bob"}
e1 := Employee{Id:"0",Name:"Bob"}
e2 := new(Employee) //注意这里返回的引用/指针,相当于 e:= &Employee{}
e2.Id = "2" //与其他主要编程语言的差异:通过示例的指针访问成员不需要使用 ->
e2.Name = "Lisi"

示例代码

package encapsulation

import "testing"

type Employee struct {
	Id string
	Name string
	Age int
}

func TestCreateEmployee(t *testing.T)  {
	e := Employee{"0","Bob",20}
	e1 := Employee{Name:"Bob",Age:20}
	e2 := new(Employee)
	e2.Id = "2"
	e2.Name = "Rose"
	t.Log(e)
	t.Log(e1)
	t.Log(e1.Id)
	t.Log(e2)
	t.Logf("e is %T",e)
	t.Logf("e2 is %T",e2)
}

输出

=== RUN   TestCreateEmployee
--- PASS: TestCreateEmployee (0.00s)
    encap_test.go:17: {0 Bob 20}
    encap_test.go:18: { Bob 20}
    encap_test.go:19: 
    encap_test.go:20: &{2 Rose 0}
    encap_test.go:21: e is encapsulation.Employee
    encap_test.go:22: e2 is *encapsulation.Employee
PASS

Process finished with exit code 0

行为(方法)定义

//第一种定义方式在实例对应方法被调用时,实例的成员会进行值复制
func (e Employee) String() string {
	return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}

//通常情况下为了避免内存拷贝我们使用第二种定义方式
func (e *Employee) String() string {
	return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}

示例代码 第二种

func (e *Employee) String() string {
	fmt.Printf("Address is %x\n", unsafe.Pointer(&e.Name))
	return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}

func TestStructOperations(t *testing.T) {
	e := Employee{"0", "Bob", 20}
	fmt.Printf("Address is %x\n", unsafe.Pointer(&e.Name))
	t.Log(e.String())
}

输出

=== RUN   TestStructOperations
Address is c000066370
Address is c000066370
--- PASS: TestStructOperations (0.00s)
    encap_test.go:41: ID:0-Name:Bob-Age:20
PASS

Process finished with exit code 0

第一种

func (e Employee) String() string {
	fmt.Printf("Address is %x\n", unsafe.Pointer(&e.Name))
	return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}

func TestStructOperations(t *testing.T) {
	e := Employee{"0", "Bob", 20}
	fmt.Printf("Address is %x\n", unsafe.Pointer(&e.Name))
	t.Log(e.String())
}

输出

=== RUN   TestStructOperations
Address is c000066370
Address is c0000b6010
--- PASS: TestStructOperations (0.00s)
    encap_test.go:42: ID:0-Name:Bob-Age:20
PASS

Process finished with exit code 0

可以看出两份代码的输出结果是不一样的,第二种方式内存地址并没有改变,而第一种方式的内存地址是改变了的,第一种方法因为数据的复制,就会引起多余的开销,小心使用

示例代码请访问: github.com/wenjianzhan…