Go 언어에서 제공하는 구조는 다양한 데이터 유형을 사용하여 정의된 다양한 변수를 결합한 고급 데이터 유형입니다. 더 이상 고민하지 않고 예를 살펴보겠습니다.
type Rect struct { width float64 length float64}
위에서 우리는 직사각형 구조를 정의했습니다. 먼저, 키는 유형입니다. 이는 새로운 데이터 유형이 정의되어야 함을 의미하며, 그 다음에는 새로운 데이터 유형 이름 Rect가 정의됩니다. 마지막으로 struct 키워드는 이 고급 데이터 유형이 구조 유형임을 나타냅니다. 위의 예에서는 너비와 길이가 동일한 데이터 유형이므로 다음 형식으로 작성할 수도 있습니다.
type Rect struct { width, length float64}
좋아요, 구조를 사용하여 직사각형의 면적을 계산해 보겠습니다.
package main import ( "fmt" ) type Rect struct { width, length float64 } func main() { var rect Rect rect.width = 100 rect.length = 200 fmt.Println(rect.width * rect.length) }
위의 예에서 볼 수 있듯이 구조 유형은 실제로 기본 데이터 유형과 동일한 방식으로 사용됩니다. 유일한 차이점은 구조 유형이 내부 멤버에 액세스할 수 있다는 것입니다. 내부 구성원에게 값을 할당하고 내부 구성원 값을 읽는 것을 포함합니다.
위의 예에서는 var 키워드를 사용하여 먼저 Rect 변수를 정의한 다음 해당 멤버에 값을 할당했습니다. 초기화를 사용하여 Rect 변수의 내부 멤버에 값을 할당할 수도 있습니다.
package main import ( "fmt" ) type Rect struct { width, length float64 } func main() { var rect = Rect{width: 100, length: 200} fmt.Println(rect.width * rect.length) }
물론 구조체 멤버가 정의된 순서를 알고 있다면 key:value 메서드를 사용하지 않고 정의된 순서대로 구조체 멤버에 직접 값을 할당할 수도 있습니다.
package main import ( "fmt" ) type Rect struct { width, length float64 } func main() { var rect = Rect{100, 200} fmt.Println("Width:", rect.width, "* Length:", rect.length, "= Area:", rect.width*rect.length) }
출력 결과는
Width: 100 * Length: 200 = Area: 20000
구조 매개변수 전달 방법
Go 함수의 매개변수 전달 방법이 값 전달이라고 말씀드렸는데요. 구조에 중요한 신체에도 적용 가능합니다.
package main import ( "fmt" ) type Rect struct { width, length float64 } func double_area(rect Rect) float64 { rect.width *= 2 rect.length *= 2 return rect.width * rect.length } func main() { var rect = Rect{100, 200} fmt.Println(double_area(rect)) fmt.Println("Width:", rect.width, "Length:", rect.length) }
위 예제의 출력은 다음과 같습니다.
80000 Width: 100 Length: 200
즉, double_area 함수에서 구조의 너비와 길이를 두 배로 늘렸지만 여전히 기본 변수의 ret 변수의 너비와 길이에는 영향을 미치지 않습니다. 기능.
구조 조합 함수
위에서는 메인 함수에서 직사각형의 면적을 계산했지만, 직사각형의 면적을 의 "내부 함수"로 제공하면 더 좋을 것 같다는 생각이 듭니다. 직사각형 구조. 이렇게 하면 가로와 세로를 따로 계산할 필요 없이 이 직사각형의 넓이가 얼마인지 직접적으로 알 수 있습니다. 이제 구조체의 "내부 함수"를 정의하는 방법을 살펴보겠습니다.
package main import ( "fmt" ) type Rect struct { width, length float64 } func (rect Rect) area() float64 { return rect.width * rect.length } func main() { var rect = Rect{100, 200} fmt.Println("Width:", rect.width, "Length:", rect.length, "Area:", rect.area()) }
eh? Rect 데이터 유형 내부에 전혀 정의되지 않은 "내부 메소드"는 무엇입니까?
실제로, 메인 함수의 ret 변수가 사각형 영역을 얻기 위해 Area() 함수를 직접 호출할 수 있지만 실제로는 Area() 함수가 Rect 구조 내부에 정의되어 있지 않다는 것을 알 수 있습니다. 이는 C와 매우 다릅니다. 언어. 큰 차이. Go는 구성 함수를 사용하여 구조에 대한 구조 방법을 정의합니다. 위의 Area() 함수 정의를 자세히 살펴보겠습니다.
첫 번째는 이것이 함수임을 나타내는 키워드 func이고, 두 번째 매개 변수는 구조체 유형과 인스턴스 변수, 세 번째는 함수 이름, 네 번째는 함수 반환 값입니다. 여기서 우리는 Area() 함수와 일반 함수 정의의 차이점이 Area() 함수에 추가 구조 유형 한정이 있다는 점을 알 수 있습니다. 이런 식으로 Go는 이것이 구조에 대해 정의된 메서드라는 것을 알고 있습니다.
여기서 한 가지 주목할 점은 구조체에 정의된 함수를 일반적으로 메소드라고 부른다는 것입니다.
구조체와 포인터
포인터 섹션에서 포인터의 주요 기능은 함수 내에서 전달된 변수의 값을 변경하는 것이라고 언급했습니다. 직사각형의 면적을 계산하는 위의 예에서 다음과 같이 코드를 수정할 수 있습니다.
package main import ( "fmt" ) type Rect struct { width, length float64 } func (rect *Rect) area() float64 { return rect.width * rect.length } func main() { var rect = new(Rect) rect.width = 100 rect.length = 200 fmt.Println("Width:", rect.width, "Length:", rect.length, "Area:", rect.area()) }
위의 예에서 새 함수는 구조 포인터 ect를 생성하는 데 사용됩니다. 즉, ret의 유형은 다음과 같습니다. *Rect 및 구조가 포인터와 관련하여 구조의 멤버에 액세스하기 위해 *를 사용할 필요가 없으며 .reference를 직접 사용하면 됩니다. 따라서 위의 예에서는 ect.width=100 및 ret.length=200을 직접 사용하여 구조체 멤버 값을 설정했습니다. 이때 ret은 구조체 포인터이기 때문에 Area() 함수를 정의할 때 구조체는 *Rect로 제한됩니다.
실제로 면적을 계산하는 이 예에서는 직사각형의 너비나 길이를 변경할 필요가 없으므로 면적 함수를 정의할 때 구조 유형을 Rect로 제한해도 괜찮습니다. 다음과 같습니다:
package main import ( "fmt" ) type Rect struct { width, length float64 } func (rect Rect) area() float64 { return rect.width * rect.length } func main() { var rect = new(Rect) rect.width = 100 rect.length = 200 fmt.Println("Width:", rect.width, "Length:", rect.length, "Area:", rect.area()) }
Go는 여기서 충분히 똑똑하므로 ret.area()도 가능합니다.
구조체 포인터를 사용하지 않는 것과 포인터를 사용하지 않는 것의 시작점은 동일합니다. 즉, 함수 내부에서 전달된 매개변수의 값을 변경하려고 하는지 여부입니다. 또 다른 예는 다음과 같습니다.
package main import ( "fmt" ) type Rect struct { width, length float64 } func (rect *Rect) double_area() float64 { rect.width *= 2 rect.length *= 2 return rect.width * rect.length } func main() { var rect = new(Rect) rect.width = 100 rect.length = 200 fmt.Println(*rect) fmt.Println("Double Width:", rect.width, "Double Length:", rect.length, "Double Area:", rect.double_area()) fmt.Println(*rect) }
이 예의 출력은 다음과 같습니다.
{100 200}
이중 너비: 200 이중 길이: 400 이중 영역: 80000
{200 400}
구조에 포함된 유형
구조 내부에서 다른 구조 유형의 멤버를 정의할 수 있습니다. 예를 들어 iPhone도 전화입니다. 예를 살펴보겠습니다.
package main import ( "fmt" ) type Phone struct { price int color string } type IPhone struct { phone Phone model string } func main() { var p IPhone p.phone.price = 5000 p.phone.color = "Black" p.model = "iPhone 5" fmt.Println("I have a iPhone:") fmt.Println("Price:", p.phone.price) fmt.Println("Color:", p.phone.color) fmt.Println("Model:", p.model) }
출력 결과는 다음과 같습니다.
I have a iPhone: Price: 5000 Color: Black Model: iPhone 5
在上面的例子中,我们在结构体IPhone里面定义了一个Phone变量phone,然后我们可以像正常的访问结构体成员一样访问phone的成员数据。但是我们原来的意思是“iPhone也是(is-a)Phone”,而这里的结构体IPhone里面定义了一个phone变量,给人的感觉就是“iPhone有一个(has-a)Phone”,挺奇怪的。当然Go也知道这种方式很奇怪,所以支持如下做法:
package main import ( "fmt" ) type Phone struct { price int color string } type IPhone struct { Phone model string } func main() { var p IPhone p.price = 5000 p.color = "Black" p.model = "iPhone 5" fmt.Println("I have a iPhone:") fmt.Println("Price:", p.price) fmt.Println("Color:", p.color) fmt.Println("Model:", p.model) }
输出结果为
I have a iPhone: Price: 5000 Color: Black Model: iPhone 5
在这个例子中,我们定义IPhone结构体的时候,不再定义Phone变量,直接把结构体Phone类型定义在那里。然后IPhone就可以像访问直接定义在自己结构体里面的成员一样访问Phone的成员。
上面的例子中,我们演示了结构体的内嵌类型以及内嵌类型的成员访问,除此之外,假设结构体A内部定义了一个内嵌结构体B,那么A同时也可以调用所有定义在B上面的函数。
package main import ( "fmt" ) type Phone struct { price int color string } func (phone Phone) ringing() { fmt.Println("Phone is ringing...") } type IPhone struct { Phone model string } func main() { var p IPhone p.price = 5000 p.color = "Black" p.model = "iPhone 5" fmt.Println("I have a iPhone:") fmt.Println("Price:", p.price) fmt.Println("Color:", p.color) fmt.Println("Model:", p.model) p.ringing() }
输出结果为:
I have a iPhone: Price: 5000 Color: Black Model: iPhone 5 Phone is ringing...
接口
我们先看一个例子,关于Nokia手机和iPhone手机都能够打电话的例子。
package main import ( "fmt" ) type NokiaPhone struct { } func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!") } type IPhone struct { } func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!") } func main() { var nokia NokiaPhone nokia.call() var iPhone IPhone iPhone.call() }
我们定义了NokiaPhone和IPhone,它们都有各自的方法call(),表示自己都能够打电话。但是我们想一想,是手机都应该能够打电话,所以这个不算是NokiaPhone或是IPhone的独特特点。否则iPhone不可能卖这么贵了。
再仔细看一下接口的定义,首先是关键字type,然后是接口名称,最后是关键字interface表示这个类型是接口类型。在接口类型里面,我们定义了一组方法。
Go语言提供了一种接口功能,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口,不一定非要显式地声明要去实现哪些接口啦。比如上面的手机的call()方法,就完全可以定义在接口Phone里面,而NokiaPhone和IPhone只要实现了这个接口就是一个Phone。
package main import ( "fmt" ) type Phone interface { call() } type NokiaPhone struct { } func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!") } type IPhone struct { } func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!") } func main() { var phone Phone phone = new(NokiaPhone) phone.call() phone = new(IPhone) phone.call() }
在上面的例子中,我们定义了一个接口Phone,接口里面有一个方法call(),仅此而已。然后我们在main函数里面定义了一个Phone类型变量,并分别为之赋值为NokiaPhone和IPhone。然后调用call()方法,输出结果如下:
I am Nokia, I can call you!
I am iPhone, I can call you!
以前我们说过,Go语言式静态类型语言,变量的类型在运行过程中不能改变。但是在上面的例子中,phone变量好像先定义为Phone类型,然后是NokiaPhone类型,最后成为了IPhone类型,真的是这样吗?
原来,在Go语言里面,一个类型A只要实现了接口X所定义的全部方法,那么A类型的变量也是X类型的变量。在上面的例子中,NokiaPhone和IPhone都实现了Phone接口的call()方法,所以它们都是Phone,这样一来是不是感觉正常了一些。
我们为Phone添加一个方法sales(),再来熟悉一下接口用法。
package main import ( "fmt" ) type Phone interface { call() sales() int } type NokiaPhone struct { price int } func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!") } func (nokiaPhone NokiaPhone) sales() int { return nokiaPhone.price } type IPhone struct { price int } func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!") } func (iPhone IPhone) sales() int { return iPhone.price } func main() { var phones = [5]Phone{ NokiaPhone{price: 350}, IPhone{price: 5000}, IPhone{price: 3400}, NokiaPhone{price: 450}, IPhone{price: 5000}, } var totalSales = 0 for _, phone := range phones { totalSales += phone.sales() } fmt.Println(totalSales) }
输出结果:
14200
上面的例子中,我们定义了一个手机数组,然后计算手机的总售价。可以看到,由于NokiaPhone和IPhone都实现了sales()方法,所以它们都是Phone类型,但是计算售价的时候,Go会知道调用哪个对象实现的方法。
接口类型还可以作为结构体的数据成员。
假设有个败家子,iPhone没有出的时候,买了好几款Nokia,iPhone出来后,又买了好多部iPhone,老爸要来看看这小子一共花了多少钱。
import ( "fmt" ) type Phone interface { sales() int } type NokiaPhone struct { price int } func (nokiaPhone NokiaPhone) sales() int { return nokiaPhone.price } type IPhone struct { price int } func (iPhone IPhone) sales() int { return iPhone.price } type Person struct { phones []Phone name string age int } func (person Person) total_cost() int { var sum = 0 for _, phone := range person.phones { sum += phone.sales() } return sum } func main() { var bought_phones = [5]Phone{ NokiaPhone{price: 350}, IPhone{price: 5000}, IPhone{price: 3400}, NokiaPhone{price: 450}, IPhone{price: 5000}, } var person = Person{name: "Jemy", age: 25, phones: bought_phones[:]} fmt.Println(person.name) fmt.Println(person.age) fmt.Println(person.total_cost()) }
这个例子纯为演示接口作为结构体数据成员,如有雷同,纯属巧合。这里面我们定义了一个Person结构体,结构体内部定义了一个手机类型切片。另外我们定义了Person的total_cost()方法用来计算手机花费总额。输出结果如下:
Jemy
25
14200
更多go语言知识请关注PHP中文网go语言教程栏目。
위 내용은 go 언어 구조 조합 기능 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!