第六章 Go语言面向对象编程
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.
在 Go 语言里是不支持继承的,Go 语言接口的实现使用了类似于duck type
的一种实现方式,与其他主流语言都不一样。
封装数据和行为
结构体定义
type Employee struct {
Id string
Name string
Age int
}
实例创建及初始化
测试实例 1
func TestCreateEmployeeObj(t *testing.T) {
e := Employee{"0", "Bob", 20}
e1 := Employee{Name: "Mike", Age: 30}
// 注意这里返回的引用/指针,相当于 e := &Employee{}
e2 := new(Employee)
// 与其他主要编程语言的差异:通过实例的指针访问成员不需要使用 ->
e2.Id = "2"
e2.Age = 22
e2.Name = "Rose"
t.Log(e) // {0 Bob 20}
t.Log(e1) // { Mike 30}
t.Log(e1.Id) //
t.Log(e2) // &{2 Rose 22}
// %T 输出实例的类型
t.Logf("e is %T", e) // e is encapsulation.Employee
t.Logf("e2 is %T", e2) // e2 is *encapsulation.Employee
}
行为(方法)定义
与其他主要编程语言的差异
// 第一种定义方式在实例对应方法被调用时,实例的成员会进行值复制
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 TestStructOperation(t *testing.T) {
e := &Employee{"0", "Bob", 20}
t.Log(e.String())
}
是可以的,通过一个类型是指针的实例,调用它的成员或者方法,是不需要使用箭头符号的,直接使用点。
测试代码 2
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 TestStructOperation(t *testing.T) {
e := &Employee{"0", "Bob", 20}
fmt.Printf("Address is %x\n", unsafe.Pointer(&e.Name))
t.Log(e.String())
}
// 输出:
// Address is c000092370
// Address is c000092370
// encap_test.go:48: ID:0-Name:Bob-Age:20
// 使用这种方式,并没有产生实例的复制,推荐使用
测试代码 3
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 TestStructOperation(t *testing.T) {
e := Employee{"0", "Bob", 20}
fmt.Printf("Address is %x\n", unsafe.Pointer(&e.Name))
t.Log(e.String())
}
// 输出:
// Address is c000092370
// Address is c0000923a0
// encap_test.go:49: ID:0-Name:Bob-Age:20
// 使用这种方式,产生了实例的复制,因此不推荐使用