Heim  >  Artikel  >  Backend-Entwicklung  >  Einführung in die Sprachstrukturkombinationsfunktion von Go

Einführung in die Sprachstrukturkombinationsfunktion von Go

尚
nach vorne
2020-01-13 17:14:335585Durchsuche

Einführung in die Sprachstrukturkombinationsfunktion von Go

Die von der Go-Sprache bereitgestellte Struktur ist ein erweiterter Datentyp, der verschiedene Variablen kombiniert, die mithilfe verschiedener Datentypen definiert sind. Schauen wir uns ohne weiteres das Beispiel an:

type Rect struct {
	width float64
	length float64}

Oben haben wir eine rechteckige Struktur definiert. Der erste Schlüssel ist, dass Typ bedeutet, dass ein neuer Datentyp definiert werden soll, dann der neue Datentypname Rect, und Das Schlüsselwort struct gibt schließlich an, dass dieser erweiterte Datentyp ein Strukturtyp ist. Da Breite und Länge im obigen Beispiel denselben Datentyp haben, können sie auch im folgenden Format geschrieben werden:

type Rect struct {
	width, length float64}

Okay, lass uns etwas mit der Struktur machen.

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)
}

Wie Sie dem obigen Beispiel entnehmen können, wird der Strukturtyp tatsächlich auf die gleiche Weise wie der Basisdatentyp verwendet. Der einzige Unterschied besteht darin, dass der Strukturtyp über auf die internen Mitglieder zugreifen kann. Einschließlich der Zuweisung von Werten zu internen Mitgliedern und dem Lesen interner Mitgliedswerte.

Im obigen Beispiel verwenden wir das Schlüsselwort var, um zunächst eine Rect-Variable zu definieren und dann ihren Mitgliedern Werte zuzuweisen. Wir können die Initialisierung auch verwenden, um den internen Mitgliedern der Rect-Variablen Werte zuzuweisen.

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)
}

Wenn Sie die Reihenfolge kennen, in der die Strukturelemente definiert sind, können Sie den Strukturelementen natürlich auch direkt Werte in der Reihenfolge zuweisen, in der sie definiert sind, ohne den Schlüssel:Wert zu verwenden Verfahren.

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)
}

Das Ausgabeergebnis ist

Breite: 100 * Länge: 200 = Fläche: 20000

Methode zur Übergabe von Strukturparametern

Wir haben gesagt, dass die Parameterübergabemethode der Go-Funktion die Wertübergabe ist, und dieser Satz gilt auch für Strukturen.

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)
}

Die Ausgabe des obigen Beispiels ist:

80000
Width: 100 Length: 200

Das heißt, obwohl wir die Breite und Länge der Struktur in der Funktion double_area verdoppeln, hat dies immer noch keine Auswirkungen auf die Variable rect in der Hauptfunktion.

Strukturkombinationsfunktion

Oben haben wir die Fläche des Rechtecks ​​in der Hauptfunktion berechnet, aber wir haben das Gefühl, dass die Fläche des Rechtecks ​​sein kann Als „interne Funktion der rechteckigen Struktur“ wäre es besser anzubieten. Auf diese Weise können wir direkt sagen, wie groß die Fläche dieses Rechtecks ​​ist, ohne Breite und Länge separat berechnen zu müssen. Schauen wir uns nun an, wie man die „interne Funktion“ einer Struktur definiert:

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())
}

Huh? Was für eine „interne Methode“ ist das? Sie ist überhaupt nicht im Rect-Datentyp definiert?

Tatsächlich sehen wir, dass die Funktion „area()“ zwar direkt in der Hauptfunktion aufgerufen werden kann, um die rechteckige Fläche zu erhalten, die Funktion „area()“ jedoch nicht innerhalb der Rect-Struktur definiert ist. Dies ist ähnlich bis C Sprachen sind sehr unterschiedlich. Go verwendet Kompositionsfunktionen, um Strukturmethoden für Strukturen zu definieren. Schauen wir uns die Definition der Funktion „area()“ oben genauer an.

Der erste Parameter ist das Schlüsselwort func, um anzuzeigen, dass es sich um eine Funktion handelt, der zweite Parameter ist der Strukturtyp und die Instanzvariable, der dritte ist der Funktionsname und der vierte ist der Rückgabewert der Funktion. Hier können wir sehen, dass der Unterschied zwischen der Funktion „area()“ und der Definition einer gewöhnlichen Funktion darin besteht, dass die Funktion „area()“ über eine zusätzliche Strukturtypqualifizierung verfügt. Dadurch weiß Go, dass es sich um eine für die Struktur definierte Methode handelt.

Hier ist zu beachten, dass auf Strukturen definierte Funktionen im Allgemeinen als Methoden bezeichnet werden.

Strukturen und Zeiger

Wie wir im Abschnitt über Zeiger erwähnt haben, besteht die Hauptfunktion von Zeigern darin, den Wert der übergebenen Variablen innerhalb der Funktion zu ändern. Für das obige Beispiel zur Berechnung der Fläche eines Rechtecks ​​können wir den Code wie folgt ändern:

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())
}

Im obigen Beispiel wird die neue Funktion verwendet, um einen Strukturzeiger rect zu erstellen, was bedeutet, dass der Der Rect-Typ ist *Rect. Wenn eine Struktur auf einen Zeiger trifft, müssen Sie nicht * verwenden, um auf die Mitglieder der Struktur zuzugreifen, sondern einfach .reference verwenden. Im obigen Beispiel verwenden wir also direkt rect.width=100 und rect.length=200, um die Werte der Strukturelemente festzulegen. Da rect zu diesem Zeitpunkt ein Strukturzeiger ist, ist die Struktur auf *Rect beschränkt, wenn wir die Funktion „area()“ definieren.

Tatsächlich müssen wir in diesem Beispiel der Flächenberechnung weder die Breite noch die Länge des Rechtecks ​​ändern. Daher ist es bei der Definition der Flächenfunktion in Ordnung, den Strukturgrenztyp weiterhin als „Rect“ zu verwenden . Wie folgt:

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 ist hier schlau genug, daher ist auch rect.area() möglich.

Was die Nichtverwendung von Strukturzeigern und die Nichtverwendung von Zeigern angeht, ist der Ausgangspunkt derselbe, nämlich unabhängig davon, ob Sie versuchen, den Wert des übergebenen Parameters innerhalb der Funktion zu ändern. Ein weiteres Beispiel lautet wie folgt:

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)
}

Die Ausgabe dieses Beispiels ist:

{100 200}

Double Breite: 200 Double Länge: 400 Double Area: 80000

{200 400}

Struktur eingebetteter Typ

Wir können Mitglieder eines anderen Strukturtyps innerhalb einer Struktur definieren. Zum Beispiel ist das iPhone auch ein Telefon. Schauen wir uns das Beispiel an:

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)
}

Das Ausgabeergebnis ist:

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语言教程栏目。

Das obige ist der detaillierte Inhalt vonEinführung in die Sprachstrukturkombinationsfunktion von Go. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen