Maison > Article > développement back-end > Introduction à la fonction de combinaison de structure de langue
La structure fournie par le langage Go est un type de données avancé qui combine différentes variables définies à l'aide de différents types de données. Sans plus tarder, regardons l'exemple :
type Rect struct { width float64 length float64}
Ci-dessus, nous avons défini une structure rectangulaire. La première clé est que type signifie qu'un nouveau type de données doit être défini, puis le nouveau nom du type de données Rect, et Enfin, le mot-clé struct indique que ce type de données avancé est un type de structure. Dans l'exemple ci-dessus, comme la largeur et la longueur ont le même type de données, elles peuvent également être écrites dans le format suivant :
type Rect struct { width, length float64}
D'accord, faisons quelque chose avec la structure.
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) }
Comme vous pouvez le voir dans l'exemple ci-dessus, le type de structure est en fait utilisé de la même manière que le type de données de base. La seule différence est que le type de structure peut accéder aux membres internes via . Y compris l'attribution de valeurs aux membres internes et la lecture des valeurs des membres internes.
Dans l'exemple ci-dessus, nous utilisons le mot-clé var pour définir d'abord une variable Rect, puis attribuer des valeurs à ses membres. Nous pouvons également utiliser l'initialisation pour attribuer des valeurs aux membres internes de la variable 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) }
Bien sûr, si vous connaissez l'ordre dans lequel les membres de la structure sont définis, vous pouvez également attribuer des valeurs aux membres de la structure directement dans l'ordre dans lequel ils sont définis, sans utiliser la clé:valeur méthode.
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) }
Le résultat de sortie est
Largeur : 100 * Longueur : 200 = Surface : 20000
Méthode de transmission des paramètres de structure
Nous avons dit que la méthode de passage de paramètres de la fonction Go est le passage de valeurs, et cette phrase s'applique également aux structures.
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) }
Le résultat de l'exemple ci-dessus est :
80000 Width: 100 Length: 200
C'est-à-dire que même si nous doublons la largeur et la longueur de la structure dans la fonction double_area, cela n'affecte toujours pas la variable rect dans la fonction principale largeur et longueur.
Fonction de combinaison de structure
Ci-dessus nous avons calculé l'aire du rectangle dans la fonction principale, mais nous pensons que si l'aire du rectangle peut être utilisé comme « fonction interne de la structure rectangulaire » Il vaudrait mieux offrir. De cette façon, nous pouvons dire directement quelle est l'aire dece rectangle sans avoir à calculer la largeur et la longueur séparément. Voyons maintenant comment définir la « fonction interne » d’une structure :
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()) }
Hein ? De quel type de « méthode interne » s'agit-il ? Elle n'est pas du tout définie dans le type de données Rect ?
En effet, on voit que bien que la variable rect dans la fonction principale puisse appeler directement la fonction Area() pour obtenir la zone rectangulaire, la fonction Area() n'est effectivement pas définie à l'intérieur de la structure Rect. C'est similaire. Les langages C sont très différents. Go utilise des fonctions de composition pour définir des méthodes de structure pour les structures. Examinons de plus près la définition de la fonction Area() ci-dessus.
Le premier est le mot-clé func pour indiquer qu'il s'agit d'une fonction, le deuxième paramètre est le type de structure et la variable d'instance, le troisième est le nom de la fonction et le quatrième est la valeur de retour de la fonction. Ici, nous pouvons voir que la différence entre la fonction Area() et la définition de fonction ordinaire est que la fonction Area() a une qualification de type de structure supplémentaire. De cette façon, Go sait qu'il s'agit d'une méthode définie pour la structure.
Une chose à noter ici est que les fonctions définies sur les structures sont généralement appelées méthodes.
Structures et pointeurs
Comme nous l'avons mentionné dans la section sur les pointeurs, la fonction principale des pointeurs est de changer la valeur de la variable passée à l'intérieur de la fonction. Pour l'exemple ci-dessus de calcul de l'aire d'un rectangle, nous pouvons modifier le code comme suit :
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()) }
Dans l'exemple ci-dessus, la nouvelle fonction est utilisée pour créer un pointeur de structure rect, ce qui signifie que le le type de rect est *Rect Lorsqu'une structure rencontre un pointeur, vous n'avez pas besoin d'utiliser * pour accéder aux membres de la structure, utilisez simplement .reference. Ainsi, dans l'exemple ci-dessus, nous utilisons directement rect.width=100 et rect.length=200 pour définir les valeurs des membres de la structure. Parce que rect est actuellement un pointeur de structure, la structure est limitée à *Rect lorsque nous définissons la fonction Area().
En fait, dans cet exemple de calcul de l'aire, nous n'avons pas besoin de changer la largeur ou la longueur du rectangle, donc lors de la définition de la fonction d'aire, il est acceptable d'avoir toujours le type de limite de structure comme Rect . Comme suit :
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 est assez intelligent ici, donc rect.area() est également possible.
Quant à ne pas utiliser de pointeurs de structure et à ne pas utiliser de pointeurs, le point de départ est le même, c'est-à-dire si vous essayez de modifier la valeur du paramètre passé à l'intérieur de la fonction. Un autre exemple est le suivant :
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) }
Le résultat de cet exemple est :
{100 200}
Double largeur : 200 Double longueur : 400 Double surface : 80 000
{200 400}
Type intégré de structure
Nous pouvons définir des membres d'un autre type de structure à l'intérieur d'une structure. Par exemple, l'iPhone est aussi un téléphone. Regardons l'exemple :
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) }
Le résultat de sortie est :
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语言教程栏目。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!