
Une méthode est une fonction attachée à un type : elle lui donne un comportement. Ce guide vous montre comment définir des méthodes en Go et, surtout, comment choisir le récepteur : valeur ou pointeur. Ce choix décide si la méthode peut modifier l'objet, et il conditionnera plus tard la façon dont votre type satisfait une interface. Public visé : vous connaissez les structures et les pointeurs. Exemples testés sur Go 1.26.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Définir une méthode sur un type.
- Choisir un récepteur valeur ou pointeur en connaissance de cause.
- Appliquer la règle de cohérence des récepteurs.
- Attacher une méthode à un type qui n'est pas une struct.
Définir une méthode
Section intitulée « Définir une méthode »Une méthode ressemble à une fonction, avec un récepteur déclaré entre func et le nom : la variable qui représente l'objet sur lequel la méthode agit.
type Compteur struct { n int}
// (c Compteur) est le récepteur : lecture seulefunc (c Compteur) Valeur() int { return c.n}On l'appelle ensuite avec la notation pointée : c.Valeur(). Le récepteur c joue le rôle du this/self d'autres langages, mais il est nommé explicitement et vous choisissez son type.
Récepteur valeur ou pointeur : le choix qui compte
Section intitulée « Récepteur valeur ou pointeur : le choix qui compte »C'est le point central. Un récepteur valeur (c Compteur) reçoit une copie : la méthode peut lire mais pas modifier l'objet. Un récepteur pointeur (c *Compteur) reçoit l'adresse : la méthode peut modifier l'objet original.
// récepteur POINTEUR : peut modifierfunc (c *Compteur) Increment() { c.n++ }
// récepteur VALEUR : lecture seulefunc (c Compteur) Valeur() int { return c.n }
func main() { c := Compteur{} c.Increment() c.Increment() c.Increment() fmt.Println(c.Valeur()) // 3}Increment a un récepteur pointeur : chaque appel modifie bien le compteur, qui atteint 3. Si son récepteur était une valeur (c Compteur), il incrémenterait une copie jetée aussitôt, et Valeur() renverrait toujours 0. La règle pratique : une méthode qui modifie l'objet doit avoir un récepteur pointeur.
Vous avez peut-être remarqué qu'on écrit c.Increment() et non (&c).Increment(), alors que le récepteur est un pointeur. Go prend automatiquement l'adresse quand c'est nécessaire, tant que c est une variable adressable. Vous n'avez donc pas à jongler avec & à chaque appel : le compilateur s'en charge.
La règle de cohérence des récepteurs
Section intitulée « La règle de cohérence des récepteurs »Une convention forte en Go : tous les récepteurs d'un même type doivent être du même genre, soit tous valeur, soit tous pointeur. Mélanger prête à confusion et pose problème dès qu'on utilise le type derrière une interface. En pratique, dès qu'une seule méthode a besoin d'un récepteur pointeur, on met toutes les méthodes du type en récepteur pointeur.
Des méthodes sur des types non-struct
Section intitulée « Des méthodes sur des types non-struct »Une méthode n'est pas réservée aux structs. Vous pouvez en attacher à n'importe quel type nommé que vous définissez, y compris à partir d'un type de base.
type Celsius float64
func (c Celsius) EnFahrenheit() float64 { return float64(c)*9/5 + 32}
func main() { t := Celsius(37) fmt.Printf("%.1f°F\n", t.EnFahrenheit()) // 98.6°F}En nommant un type (type Celsius float64), on peut lui donner des méthodes qui expriment son domaine métier. C'est plus lisible et plus sûr qu'un float64 nu dont on ne sait pas s'il représente des degrés, des euros ou des secondes.
Exercice : un rectangle qui grandit
Section intitulée « Exercice : un rectangle qui grandit »Cet exercice met en jeu les deux types de récepteurs. Cherchez la solution avant d'ouvrir la réponse.
Créez un type Rectangle avec deux champs Largeur et Hauteur (float64), et deux méthodes :
Aire()qui renvoie la surface (largeur × hauteur), sans rien modifier.Agrandir(f float64)qui multiplie largeur et hauteur par le facteurf.
Vérifiez qu'un rectangle 3×4 a une aire de 12, puis 48 après Agrandir(2).
Indice : réfléchissez à quel récepteur (valeur ou pointeur) convient à chaque méthode selon qu'elle lit ou modifie.
Aire() ne fait que lire : récepteur valeur. Agrandir() modifie l'objet : récepteur pointeur.
package main
import "fmt"
type Rectangle struct { Largeur, Hauteur float64}
func (r Rectangle) Aire() float64 { // lecture : récepteur valeur return r.Largeur * r.Hauteur}
func (r *Rectangle) Agrandir(f float64) { // mutation : récepteur pointeur r.Largeur *= f r.Hauteur *= f}
func main() { r := Rectangle{Largeur: 3, Hauteur: 4} fmt.Println("aire:", r.Aire()) // 12 r.Agrandir(2) fmt.Println("aire:", r.Aire()) // 48}Résultat :
aire: 12aire: 48Si Agrandir avait un récepteur valeur, il modifierait une copie et l'aire resterait à 12. Par cohérence, un projet réel mettrait aussi Aire() en récepteur pointeur, puisque le type a déjà une méthode qui mute.
À retenir
Section intitulée « À retenir »- Une méthode est une fonction avec un récepteur déclaré entre
funcet le nom. - Récepteur valeur = copie en lecture seule ; récepteur pointeur = peut modifier l'objet original.
- Une méthode qui mute l'objet doit avoir un récepteur pointeur.
- Cohérence : tous les récepteurs d'un type doivent être du même genre ; en cas de doute, tout en pointeur.
- On peut attacher une méthode à tout type nommé, pas seulement aux structs, pour exprimer un domaine métier.