对象的属性行为
封装属性与行为
// 封装属性
type Employee struct {
Id string
Name string
Age int
}
// 封装行为
func (e Employee) String1() string {
return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}
func (e *Employee) String2() string {
return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}
创建并初始化
func TestCreateEmployeeObj(t *testing.T) {
//创建和初始化
e := Employee{"0", "Bob", 20}
e1 := Employee{Name: "Mike", Age: 30}
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.Logf("e is %T", e) //e is try_test.Employee,实体类型
t.Logf("e is %T", e2) //e is *try_test.Employee,指针类型
}
行为的区别
测试如下,我们使用两个方法,并输出实体类的地址,看地址是否相同
// 封装行为
func (e Employee) String1() string {
fmt.Printf("String1: Address is %x\n", unsafe.Pointer(&e.Name)) //输出地址
return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}
func (e *Employee) String2() string {
fmt.Printf("String2: 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("Origin: Address is %x\n", unsafe.Pointer(&e.Name)) //输出地址
t.Log(e.String1())
t.Log(e.String2())
}
我们发现String2的地址和最初的地址是相同的,因此我们可用得知在封装行为的时候,如果使用的是指针类型,那么执行的对象就是原有对象,如果不是指针类型,那么会进行对象的复制,即执行的对象不是原有对象。推荐使用指针类型,减少对象赋值,节约资源。
接口
- 接口为非侵入性,实现不依赖与接口定义
- 所以接口的定义可用包含在接口使用者包内
接口定义
// 接口
type Programmer interface {
WriteHelloWorld() string
}
接口实现
// 实现
type GoProgrammer struct {
}
func (gp *GoProgrammer) WriteHelloWorld() string {
return "Hello World"
}
向上转型
func TestClient(t *testing.T) {
var p Programmer = new(GoProgrammer)
t.Log(p.WriteHelloWorld()) //Hello World
}
接口变量
自定义类型
问题来源
解决方法
扩展与复用
不具有继承关系
package try_test
import (
"fmt"
"testing"
)
// 定义父类
type Pet struct {
}
func (p *Pet) Speak() {
fmt.Print("宠物: ")
}
func (p *Pet) SpeakTo(name string) {
p.Speak()
fmt.Println(name)
}
// 定义子类
type Dog struct {
p *Pet
}
// 重写
func (d *Dog) Speak() {
fmt.Print("狗: ")
}
// 复用
func (d *Dog) SpeakTo(name string) {
d.p.SpeakTo(name)
}
func TestDog(t *testing.T) {
dog := new(Dog)
dog.SpeakTo("小花") //宠物: 小花
}
多态
- 父类接口:Programmer
- 子类实现:GoProgrammer、JavaProgrammer
type Code string
// 接口
type Programmer interface {
WriteHelloWorld() Code
}
// 实现
type GoProgrammer struct {
}
func (gp *GoProgrammer) WriteHelloWorld() Code {
return "Go: Hello World"
}
type JavaProgrammer struct {
}
func (p *JavaProgrammer) WriteHelloWorld() Code {
return "Java: Hello World"
}
func WriteProgram(p Programmer) {
fmt.Printf("%T %s\n", p, p.WriteHelloWorld())//输出类型与返回值
}
func TestClient(t *testing.T) {
var p1 Programmer = new(GoProgrammer)
var p2 Programmer = new(JavaProgrammer)
WriteProgram(p1)
WriteProgram(p2)
}
空接口与断言
- 空接口可以表示任何类型
- 可以通过断言将空接口转换为指定类型
func DoSomething(p interface{}) {
//断言判断
if i, ok := p.(int); ok {
fmt.Println("Integer", i)
return
}
if s, ok := p.(string); ok {
fmt.Println("String", s)
return
}
fmt.Println("Unknow type")
}
func TestEmptyInterfaceAssertion(t *testing.T) {
DoSomething(10)
DoSomething("10")
}
Switch结构优化
func DoSomething(p interface{}) {
//断言判断
switch v := p.(type) {
case int:
fmt.Println("Integer ", v)
case string:
fmt.Println("String ", v)
default:
fmt.Println("Unknow type")
}
}
本文章参考来自Go语言从入门到实战