【Go语言】go语言简单的变量声明和结构体使用

目录

数字类型

布尔类型

字符串类型

[Rune 类型](#Rune 类型)

[Byte 类型](#Byte 类型)

类型转换

结构体

结构体定义

结构体作为函数参数

结构体方法

[1. 接收者类型](#1. 接收者类型)

[2. 方法定义](#2. 方法定义)

[3. 值接收者 vs 指针接收者](#3. 值接收者 vs 指针接收者)

[4. 调用方式的灵活性](#4. 调用方式的灵活性)

[5. 方法集](#5. 方法集)

[6. 方法与继承](#6. 方法与继承)

[7. 方法声明的位置](#7. 方法声明的位置)


在Go语言中,基础类型(也称为原始类型)是构建更复杂数据结构的基石。Go语言的基础类型可以分为几个主要类别,包括数字类型、布尔类型、和字符串类型。下面,我将为您详细介绍这些类型及其声明方法,帮助您更好地理解和使用它们。

数字类型

在Go语言中,整数类型分为有符号和无符号,各自的范围也不同。这里给出一些整数类型的声明示例:

Go 复制代码
	var i int = 1
	var i8 int8 = 127
	var i16 int16 = 32767
	var i32 int32 = -2147483648
	var i64 int64 = 9223372036854775807
	
	var u uint = 1
	var u8 uint8 = 255
	var u16 uint16 = 65535
	var u32 uint32 = 4294967295
	var u64 uint64 = 18446744073709551615
	
	

	var f32 float32 = 3.1415926
	var f64 float64 = 3.1414926564565

布尔类型

布尔类型表示逻辑值,只有truefalse两个值。

Go 复制代码
	var b bool = true
	if b {
		fmt.Println("b 是真")
	} else {
		fmt.Println("b 是假")
	}

字符串类型

Go 复制代码
var s string = "Hello, Go!" // 字符串类型

Rune 类型

rune类型代表一个Unicode码点。它是int32的别名,用于处理Unicode字符。

Go 复制代码
var r rune = '中' // 使用单引号声明rune类型,代表一个Unicode字符
fmt.Printf("%T, %d\n", r, r) // 输出: int32, 20013('中'的Unicode码点)

Byte 类型

byte类型是uint8的别名,常用于处理ASCII字符或字节数据。

Go 复制代码
var b byte = 'A' // 使用单引号声明byte类型,代表一个ASCII字符
fmt.Printf("%T, %d\n", b, b) // 输出: uint8, 65('A'的ASCII码)

类型转换

Go语言中的类型转换需要显式进行。这是因为Go语言在设计时强调类型安全,避免了隐式类型转换可能带来的问题。

Go 复制代码
var i int = 42
var f float64 = float64(i) // 将int类型显式转换为float64类型
var u uint = uint(f) // 将float64类型显式转换为uint类型
fmt.Println(i, f, u) // 输出: 42 42 42

结构体

结构体定义

Go 复制代码
type Person struct {
	name string
	Age  int
}

结构体作为函数参数

Go 复制代码
type Person struct {
	name string
	Age  int
}

func testPersion(p1 Person) Person {
	var p Person
	p.name = "Alice"
	p.Age = 18
	return p
}

结构体方法

Go 复制代码
package main

import "fmt"

type Circle struct {
    Radius float64
}

// Circle类型的方法,计算圆的面积
func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

func main() {
    circle := Circle{Radius: 5}
    fmt.Println("Circle area:", circle.Area()) // 输出:Circle area: 78.5
}

1. 接收者类型

  • 值接收者:方法通过值接收其所属类型的一个副本。这意味着方法内部对接收者的任何修改都不会影响原始实例。
  • 指针接收者:方法通过指针接收其所属类型,允许直接修改原始实例的字段。使用指针接收者还可以避免在调用方法时复制大型结构体,从而提高效率。

2. 方法定义

方法定义与函数类似,但在函数名之前添加了一个额外的参数,称为接收者,表示该方法附加到的类型。

3. 值接收者 vs 指针接收者

  • 使用值接收者定义的方法在调用时,接收者会被复制一份。这适用于小型结构体或你不希望方法能修改其接收者的场景。
  • 使用指针接收者可以让方法修改其接收者指向的值。这对于需要修改接收者,或避免在每次方法调用时复制大型结构体时非常有用。

4. 调用方式的灵活性

  • 即使方法的接收者是值类型,你也可以使用指针来调用该方法,Go语言会自动解引用。
  • 同样,如果方法的接收者是指针类型,你也可以直接使用值类型调用该方法,Go语言会自动取地址。

这个特性使得Go语言在处理方法调用时非常灵活。下面是一个示例,展示了如何定义和使用值接收者和指针接收者的方法,以及如何通过值类型和指针类型来调用这些方法。

Go 复制代码
package main

import "fmt"

type MyStruct struct {
    Value int
}

// 使用值接收者定义的方法
func (s MyStruct) ValueReceiverMethod() {
    fmt.Printf("ValueReceiverMethod 被调用, Value: %d\n", s.Value)
}

// 使用指针接收者定义的方法
func (s *MyStruct) PointerReceiverMethod() {
    fmt.Printf("PointerReceiverMethod 被调用, Value: %d\n", s.Value)
}

func main() {
    value := MyStruct{Value: 5}

    // 通过值类型调用值接收者的方法
    value.ValueReceiverMethod()

    // 通过值类型调用指针接收者的方法
    // Go语言自动取value的地址,使其符合方法的接收者类型
    value.PointerReceiverMethod()

    pointer := &MyStruct{Value: 10}

    // 通过指针类型调用值接收者的方法
    // Go语言自动解引用pointer,使其符合方法的接收者类型
    pointer.ValueReceiverMethod()

    // 通过指针类型调用指针接收者的方法
    pointer.PointerReceiverMethod()
}

在这个示例中,MyStruct定义了两个方法:ValueReceiverMethod使用值接收者,而PointerReceiverMethod使用指针接收者。

  • ValueReceiverMethod通过结构体实例的值调用时,Go语言直接使用这个值。
  • PointerReceiverMethod通过结构体实例的值调用时,Go自动取这个值的地址,以适应方法的指针接收者。
  • 同样,当ValueReceiverMethod通过结构体的指针调用时,Go自动解引用这个指针,以适应方法的值接收者。
  • PointerReceiverMethod通过结构体的指针调用时,直接使用这个指针,因为它与方法的接收者类型匹配。

5. 方法集

  • 值类型的变量可以调用值接收者和指针接收者的方法。
  • 指针类型的变量也可以调用值接收者和指针接收者的方法。
  • 但是,接口类型的变量只能调用它的方法集内的方法。值接收者方法属于类型的值和指针的方法集,而指针接收者方法只属于类型指针的方法集

代码定义:

Go 复制代码
package main

import "fmt"

// Shaper 接口,包含一个方法 ShapeName
type Shaper interface {
    ShapeName() string
}

// Square 结构体
type Square struct {
    Name string
}

// Circle 结构体
type Circle struct {
    Name string
}

// 使用值接收者实现 Shaper 接口的 ShapeName 方法
func (s Square) ShapeName() string {
    return "Square: " + s.Name
}

// 使用指针接收者实现 Shaper 接口的 ShapeName 方法
func (c *Circle) ShapeName() string {
    return "Circle: " + c.Name
}
Go 复制代码
func main() {
    square := Square{Name: "MySquare"}
    circle := Circle{Name: "MyCircle"}

    // 值类型的Square可以直接赋值给Shaper接口
    var s Shaper = square
    fmt.Println(s.ShapeName())

    // 指针类型的Circle也可以赋值给Shaper接口
    var c Shaper = &circle
    fmt.Println(c.ShapeName())

    // 注意: 直接使用值类型的Circle赋值给Shaper接口会引发编译错误
    // var cError Shaper = circle
    // fmt.Println(cError.ShapeName())

    // 直接通过值类型调用指针接收者的方法是可以的,Go会自动取地址
    fmt.Println(circle.ShapeName())

    // 通过指针类型调用值接收者的方法也是可以的,Go会自动解引用
    circlePtr := &square
    fmt.Println(circlePtr.ShapeName())
}
  • Square使用值接收者实现了Shaper接口的ShapeName方法,所以Square的值和指针类型都可以赋值给Shaper接口变量。
  • Circle使用指针接收者实现了Shaper接口的ShapeName方法,所以只有Circle的指针类型可以赋值给Shaper接口变量。如果尝试将Circle的值类型直接赋值给接口变量,会引发编译错误,因为Circle值类型的方法集不包括ShapeName方法。
  • 这个例子展示了如何根据方法的接收者类型(值接收者或指针接收者)以及它们如何匹配接口的方法集,来决定是否可以将一个类型的实例赋值给接口。

通过这个例子,你应该能更清楚地理解方法集和接口之间的关系,以及它们是如何影响Go语言中类型的行为的。掌握这些概念对于高效使用Go语言是非常重要的。继续探索和实践,你将能更深入地理解Go语言的面向对象特性。

6. 方法与继承

Go语言没有提供传统的继承机制,但可以通过嵌入结构体来实现类似继承的效果。方法也可以通过这种方式被嵌入的结构体所继承。

在Go语言中,虽然没有类似于传统面向对象语言的继承机制,但通过结构体的嵌入(有时也称为组合)和接口,我们可以实现类似继承的功能,包括方法的重用。这种方式提供了更灵活和更安全的代码组织形式。下面是一个示例,演示如何通过嵌入结构体来实现方法的"继承"。

假设我们有一个Car结构体,它有一个Drive方法,然后我们希望ElectricCar结构体"继承"Car的方法。

Go 复制代码
package main

import "fmt"

// Car 基础结构体
type Car struct {
    Name string
}

// Car的Drive方法
func (c Car) Drive() {
    fmt.Println(c.Name, "is driving.")
}

// ElectricCar 嵌入了Car
type ElectricCar struct {
    Car   // 嵌入Car结构体
    BatteryLife int
}

func main() {
    eCar := ElectricCar{
        Car:        Car{Name: "Tesla Model S"},
        BatteryLife: 300,
    }

    // 通过ElectricCar直接调用Car的Drive方法
    eCar.Drive()

    // 也可以通过嵌入的Car结构体字段调用
    eCar.Car.Drive()

    // 访问ElectricCar的字段
    fmt.Println("Battery life:", eCar.BatteryLife)

    // 访问嵌入的Car结构体的字段
    fmt.Println("Car name:", eCar.Name) // 直接访问,Go自动解析到eCar.Car.Name
    fmt.Println("Car name:", eCar.Car.Name) // 显式访问
}
  • ElectricCar通过嵌入Car结构体,"继承"了Car的方法和属性。这意味着,即使没有显式定义Drive方法,ElectricCar的实例也能调用Drive方法。
  • 通过这种嵌入机制,我们可以很容易地重用Car的功能,同时为ElectricCar添加特有的属性和方法,例如BatteryLife属性。
  • 你可以通过嵌入结构体的实例直接访问其公开的字段和方法,就像它们是外层结构体的一部分一样。在上面的示例中,eCar.Name实际上是对eCar.Car.Name的访问简写。
  • 如果需要,嵌入的结构体也可以被显式地引用,如eCar.Car.Drive()

这种方式允许Go语言开发者在没有传统类继承的情况下实现非常灵活的类型设计。通过组合和接口,你可以构建出清晰、易于理解和维护的代码结构。这种方法强调的是,"有什么"(组合)而不是"是什么"(继承),更加注重对象的能力而非其身份。继续探索Go的类型系统,你会发现更多有趣且实用的设计模式。

7. 方法声明的位置

方法可以在类型定义内部或外部声明,但是接收者的类型必须在同一个包内。这意味着你不能为内置类型或来自其他包的类型定义方法。

为自定义类型声明方法

Go 复制代码
package main

import "fmt"

// 在main包中定义了一个简单的结构体类型Person
type Person struct {
    Name string
}

// 为Person类型声明一个方法SayHello,这是允许的,因为Person在同一个包内定义
func (p Person) SayHello() {
    fmt.Println("Hello,", p.Name)
}

func main() {
    person := Person{Name: "张三"}
    person.SayHello() // 调用SayHello方法
}

在这个例子中,Person类型和它的方法SayHello都是在main包中定义的。这完全符合Go语言的规则。

尝试为内置类型或其他包的类型添加方法

假设我们想为内置的int类型添加一个新方法。尝试这样做会遇到编译错误,因为int是一个内置类型,不在用户自定义的包中。

Go 复制代码
func (i int) IsPositive() bool {
    return i > 0
}

同样地,如果你试图为来自其他包的类型定义方法,也会遇到问题。例如,尝试为time.Time类型(定义在time包中)添加一个方法:

Go 复制代码
package main

import (
    "fmt"
    "time"
)

// 尝试为time包中的Time类型添加一个新方法Display
func (t time.Time) Display() {
    fmt.Println("The time is:", t)
}

func main() {
    now := time.Now()
    now.Display() // 这里会引发编译错误
}

上述代码尝试为time.Time类型添加一个Display方法。因为Time类型定义在time包中,而不是当前的包(即使是main包),所以这将导致编译错误。

这些限制确保了Go语言包的封装性和独立性,防止了对已有类型的随意扩展,促进了代码的清晰性和维护性。要为其他包中的类型添加新的行为,通常的做法是在你自己的包中定义一个新的类型,这个新类型可以嵌入原有类型或者作为原有类型的一个字段存在。

相关推荐
学步_技术6 分钟前
Python编码系列—Python组合模式:构建灵活的对象组合
开发语言·python·组合模式
ac-er888820 分钟前
在Flask中处理后台任务
后端·python·flask
ac-er888826 分钟前
Flask中的钩子函数
后端·python·flask
o独酌o30 分钟前
递归的‘浅’理解
java·开发语言
Book_熬夜!33 分钟前
Python基础(六)——PyEcharts数据可视化初级版
开发语言·python·信息可视化·echarts·数据可视化
m0_631270401 小时前
高级c语言(五)
c语言·开发语言
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS在线文档管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
2401_858286111 小时前
53.【C语言】 字符函数和字符串函数(strcmp函数)
c语言·开发语言
Flying_Fish_roe1 小时前
Spring Boot-版本兼容性问题
java·spring boot·后端
程序猿练习生2 小时前
C++速通LeetCode中等第5题-无重复字符的最长字串
开发语言·c++·leetcode