GO学习笔记——封装、继承、多态

本文介绍了GO语言中面向对象编程的三大特性——封装、继承和多态,通过工厂模式、嵌套匿名结构体和接口实现,以及类型断言的使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GO语言有着面向对象编程的三大特性——封装、继承、多态,只是实现方式不同,本文只介绍实现方式,不会特别详细介绍特性。如果对三大特性没有概念,可以先去看面向对象编程特性相关知识。


一、封装

封装就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作才能对字段进行操作。

注意:GO开发中并没有特别强调封装,这与Java不同,不能总是用Java的语法特性来看待GO,GO本身对面向对象的特性进行了简化。

1、GO体现封装的方式

        1)对结构体中的属性进行封装

        2)通过方法、包是实现封装

2、工厂模式

GO中的结构体没有构造函数,通常使用工厂模式来实现相同功能。

工厂模式:面向对象编程的设计模式之一。在工厂模式中,创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

以下为GO中实现工厂模式的一个例子:

package pizzaPackage

type pizza struct {
	name  string
	price int
}

func (p *pizza) SetName(name string) {
	p.name = name
}

func (p *pizza) GetName() string {
	return (*p).name
}

func (p *pizza) SetPrice(price int) {
	p.price = price
}

func (p *pizza) GetPrice() int {
	return (*p).price
}

func MakePizza(name string, price int) *pizza {
	return &pizza{
		name:  name,
		price: price,
	}
}

然后在另一个包中调用这个包:

package main

import (
	"fmt"
	pizza "project2/main/pizzaPackage"
)

func main() {
	p := pizza.MakePizza("榴莲披萨", 40)
	fmt.Println("我花", p.GetPrice(), "吃到了", p.GetName())
}

代码执行结果如下:

上面我们创建了一个pizzaPackage包,但是不希望外部直接看到并使用pizza结构体的内部,所以提供了一个工厂方法MakePizza(),其他包调用这个方法就可直接得到一个pizza类型并使用这个结构体所公开的所有方法和属性,但私有的属性和方法是不可以使用的,这其实已经体现了封装的特性。

3、封装的实现步骤

1)将结构体、字段的首字母小写(private)

2)给结构体所在的包提供一个工厂模式的函数,首字母大写

3)提供一个首字母大写的Set方法,用于对属性判断与赋值

4)提供一个首字母大写的Get方法,用于获取属性的值

4、举个例子

其实上述第2点工厂模式的例子中已经体现了封装的特性与步骤,这里不另外举例了。


二、继承

1、GO中继承的实现——嵌套匿名结构体

基本语法:

type 父类结构体名 struct{

        field1 type

        field2 type

        ……

}

type 子类结构体名 struct{

        父类结构体名

        field1 type

        ……

}

在子类结构体中,父类结构体就是被嵌套的匿名结构体

2、举个例子

type Animal struct {
	Name  string
	Age   int
	Food  string
	Drink string
}

type Alive interface {
	eat()
	drink()
}

func (A *Animal) eat() {
	fmt.Println(A.Name, "吃", A.Food)
}

func (A *Animal) drink() {
	fmt.Println(A.Name, "喝", A.Drink)
}

type Dog struct {
	Animal
}

type Cat struct {
	Animal
}

func main() {
	dog := Dog{
		Animal{
			Name:  "小狗",
			Age:   5,
			Food:  "骨头",
			Drink: "水",
		},
	}

	cat := Cat{Animal{"小猫", 5, "鱼", "牛奶"}}

	dog.Animal.drink()
	dog.eat()

	cat.drink()
	cat.Animal.eat()
}

上述代码的输出为:

3、注意事项说明

1)结构体可以使用嵌套结构体的所有方法和字段,不管是否大小写

2)当只嵌套一个匿名结构体时,如果访问嵌套结构体内的字段,内部匿名结构体名可以省略,即A.B.field与A.field都可以;当结构体和匿名结构体有同名字段或方法时,编译器采用就近访问原则

3)当嵌套的匿名结构体数大于等于2时,如果这些匿名结构体有相同字段和方法并且外侧结构体没有时,必须显式的指明所访问内部结构体,即A嵌套了B和C时,A.B.field和A.C.field要指明B、C

4)当嵌套匿名结构体数大于等于2时,就实现了多重继承,但不建议使用


三、多态

GO的多态是通过接口来体现,可以按照统一的接口来调用不同的实现,这个时候接口变量就体现不同的形态。

1、GO中多态的实现——接口体现多态的两种形式

1)多态参数

声明一个接口类型的实例,然后将实现了接口的不同结构体赋值这个接口类型实例,举个例子:

type Animal struct {
	Name  string
	Age   int
	Food  string
	Drink string
}

type Alive interface {
	eat()
	drink()
}

func (A Animal) eat() {
	fmt.Println(A.Name, "吃", A.Food)
}

func (A Animal) drink() {
	fmt.Println(A.Name, "喝", A.Drink)
}

type Dog struct {
	Animal
}

type Cat struct {
	Animal
}

func main() {

	var life Alive    //声明了一个接口变量

    animal := Animal{"生物", 10, "食物", "水"}

	dog := Dog{
		Animal{
			Name:  "小狗",
			Age:   5,
			Food:  "骨头",
			Drink: "水",
		},
	}

	cat := Cat{Animal{"小猫", 5, "鱼", "牛奶"}}

	life = animal    //将实现了接口的Animal类给接口
	life.drink()
	life.eat()

	life = dog    //将实现了接口的Animal类的子类给接口
	life.drink()
	life.eat()

	life = cat
	life.drink()
	life.eat()

}

上述代码的输出为:

 

2)多态数组

用接口数组来存放不同的结构体,举个例子:

type Animal struct {
	Name  string
	Age   int
	Food  string
	Drink string
}

type Alive interface {
	eat()
	drink()
}

func (A Animal) eat() {
	fmt.Println(A.Name, "吃", A.Food)
}

func (A Animal) drink() {
	fmt.Println(A.Name, "喝", A.Drink)
}

type Dog struct {
	Animal
}

type Cat struct {
	Animal
}

func main() {

	var life [3]Alive

	life[0] = Dog{
		Animal{
			Name:  "小狗",
			Age:   5,
			Food:  "骨头",
			Drink: "水",
		},
	}

	life[1] = Cat{Animal{"小猫", 5, "鱼", "牛奶"}}

	life[2] = Animal{"生物", 10, "食物", "水"}

	for i := 0; i < 3; i++ {
		life[i].eat()
		life[i].drink()
	}

}

上述代码的输出为:

2、类型断言

由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言

举个简单的例子:

var x interface{}
var b2 float32 = 1.23
x = b2
y := x.(float32)    //float32类型断言
fmt.Printf("y的类型是 %T,值是%v",y,y)

一点说明:

        在进行类型断言时,如果类型不匹配,就会报panic,因此:

        1)进行断言时,要确保原来的空接口指向的就是断言类型

        2)或者,进行断言时,进行检测,如果成功就ok,否则也不要报panic,如下:

var x interface{}
var b2 float32 = 1.23
x = b2
y := x.(float32)    //float32类型断言

if y, ok := x.(float32);ok{
    fmt.Printf("y的类型是 %T,值是%v",y,y)
} else {
    fmt.Printf("匹配失败")
}

再举一个类型断言的常用示例(在前面Animal例子中加入一个Active()函数):

func Active(life Alive) {
	switch life.(type) {
	case Dog:
		fmt.Println("小狗汪汪叫")
	case Cat:
		fmt.Println("小猫爬上树")
	case Animal:
		fmt.Println("动物真可爱")
	default:
		fmt.Println("不明生命体")
	}

}

func main() {

	var life [3]Alive

	life[0] = Dog{
		Animal{
			Name:  "小狗",
			Age:   5,
			Food:  "骨头",
			Drink: "水",
		},
	}

	life[1] = Cat{Animal{"小猫", 5, "鱼", "牛奶"}}

	life[2] = Animal{"生物", 10, "食物", "水"}

	for i := 0; i < 3; i++ {
		Active(life[i])
	}

}

上述代码的输出为:


### Golang 中实现多态 #### 方法概述 在Golang中,虽然没有传统的继承机制,但是通过接口可以实现多态效果。只要一个类型实现了特定的接口,则该类型的值可以在任何接受此接口的地方被使用[^1]。 #### 接口定义与实现 为了展示这一特性,先定义一个简单的接口`SayHello`: ```go package utils type SayHello interface { greet() string } ``` 接着创建两个结构体`Chinese`和`American`并让它们各自实现上述接口的方法: ```go // Chinese struct with a method that satisfies the SayHello interface. type Chinese struct { Name string } func (c Chinese) greet() string { return "你好, " + c.Name } // American struct implementing the same interface differently. type American struct { Name string } func (a American) greet() string { return "Hi there, " + a.Name } ``` 以上代码片段展示了如何使不同类型遵循相同的契约——这里是打招呼的方式,但内部逻辑依据文化背景有所区别[^3]。 #### 使用多态 下面的例子进一步证明了这一点,在这里声明了一个固定大小的数组来存储不同国家的人,并遍历打印他们的问候语句: ```go import ( "fmt" ) func main() { var arr [3]utils.SayHello arr[0] = utils.Chinese{Name: "李华"} arr[1] = utils.American{Name: "Lily"} arr[2] = utils.Chinese{Name: "赵六"} for _, person := range arr { fmt.Println(person.greet()) } } ``` 这段程序会依次输出每位成员个性化的问好信息,体现了即使是在编译期已知的数据集合里也能动态决定运行时的行为模式。 #### 静态 vs 动态多态 值得注意的是,除了基于接口的动态绑定外,还有所谓的编译时常量表达式等形式属于静态多态的一部分;不过这通常不是讨论的重点所在,因为大多数情况下提到“多态”,指的就是像上面所描述的那种灵活性更高的方式[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值