Skip to content

Types

Dans la section précédente sur les types de données, nous avons brièvement présenté tous les types de données intégrés de Go. Ces types de base sont la base des types personnalisés ultérieurs. Go est un langage de typage statique typique, où les types de toutes les variables sont déterminés pendant la compilation et ne changent pas tout au long du cycle de vie du programme. Cette section présentera brièvement le système de types de Go et son utilisation de base.

Typage statique fort

Go est un langage de typage statique fort. "Statique" signifie que les types de toutes les variables en Go sont déterminés pendant la compilation et ne changent pas tout au long du cycle de vie du programme. Bien que la déclaration de variable courte en Go ressemble un peu à l'écriture d'un langage dynamique, le type de ses variables est déduit par le compilateur. La différence fondamentale est que son type ne change plus une fois déduit, tandis que les langages dynamiques sont complètement opposés. Ainsi, le code suivant ne peut pas être compilé, car a est une variable de type int et ne peut pas se voir attribuer une chaîne de caractères.

go
func main() {
  var a int = 64
  a = "64"
  fmt.Println(a) // cannot use "64" (untyped string constant) as int value in assignment
}

Le "typage fort" signifie qu'une vérification de type stricte est effectuée dans le programme. En cas d'incompatibilité de type, le programmeur est immédiatement informé qu'il ne devrait pas faire cela, au lieu d'essayer de déduire un résultat possible comme le font les langages dynamiques. Ainsi, le code suivant ne peut pas être compilé, car les deux types sont différents et ne peuvent pas être utilisés pour des opérations.

go
func main() {
  fmt.Println(1 + "1") // invalid operation: 1 + "1" (mismatched types untyped int and untyped string)
}

Déclaration de type postfixée

Pourquoi Go place-t-il la déclaration de type après plutôt qu'avant ? En grande partie, cela tire les leçons du langage C. Prenons un exemple officiel pour montrer l'effet. Voici un pointeur de fonction :

c
int (*(*fp)(int (*)(int, int), int))(int, int)

Pour être honnête, il est difficile de savoir de quel type il s'agit sans y prêter attention. En Go, une écriture similaire est la suivante :

go
f func(func(int,int) int, int) func(int, int) int

La manière de déclarer en Go suit toujours le principe selon lequel le nom vient en premier et le type vient ensuite. En lisant de gauche à droite, on peut savoir dès le premier coup d'œil qu'il s'agit d'une fonction, et que la valeur de retour est func(int,int) int. Lorsque les types deviennent de plus en plus complexes, la déclaration postfixée est bien meilleure en termes de lisibilité. La conception de Go à bien des égards est au service de la lisibilité.

Déclaration de type

En Go, la déclaration de type permet de déclarer un nouveau type avec un nom personnalisé. Déclarer un nouveau type nécessite généralement un nom de type et un type de base. Voici un exemple simple :

go
type MyInt int64

Dans la déclaration de type ci-dessus, le mot-clé type est utilisé pour déclarer un type nommé MyInt dont le type de base est int64. En Go, chaque nouveau type déclaré doit avoir un type de base correspondant, et il n'est pas recommandé que le nom du type soit en conflit avec les identifiants intégrés existants.

go
type MyInt int64

type MyFloat64 float64

type MyMap map[string]int

// Peut être compilé, mais non recommandé, cela écrasera le type original
type int int64

Les types déclarés via une déclaration de type sont tous de nouveaux types. Différents types ne peuvent pas être utilisés pour des opérations, même si leur type de base est le même.

go
type MyFloat64 float64

var f1 MyFloat64
var f float64
f1 = 0.2
f = 0.1
fmt.Println(f1 + f)
go
invalid operation: f1 + f (mismatched types MyFloat64 and float64)

Alias de type

L'alias de type est différent de la déclaration de type. Un alias de type n'est qu'un alias, il ne crée pas un nouveau type. Voici un exemple simple :

go
type Int = int

Les deux sont le même type, seul le nom est différent, ils peuvent donc être utilisés pour des opérations. Ainsi, l'exemple suivant peut naturellement être compilé.

go
type Int = int
var a Int = 1
var b int = 2
fmt.Println(a + b)
3

Les alias de type sont très utiles pour les types particulièrement complexes. Par exemple, il existe maintenant un type map[string]map[string]int, qui est une map bidimensionnelle. Voici une fonction dont le paramètre est de type map[string]map[string]int :

go
func PrintMyMap(mymap map[string]map[string]int) {
   fmt.Println(mymap)
}

Dans ce cas, il n'est pas nécessaire d'utiliser une déclaration de type, car celle-ci déclare un nouveau type qui ne peut pas être utilisé comme paramètre de cette fonction. Voici l'exemple après utilisation d'un alias de type :

go
type TwoDMap = map[string]map[string]int

func PrintMyMap(mymap TwoDMap) {
   fmt.Println(mymap)
}

L'utilisation d'un alias de type semble plus concise.

TIP

Le type intégré any est un alias de type de interface{}, les deux sont complètement équivalents, seul le nom est différent.

Conversion de type

En Go, il n'existe que des conversions de type explicites, il n'existe pas de conversions de type implicites. Par conséquent, des variables de types différents ne peuvent pas être utilisées pour des opérations et ne peuvent pas être transmises comme paramètres. La conversion de type suppose que l'on connaît le type de la variable à convertir et le type cible vers lequel convertir. Voici un exemple :

go
type MyFloat64 float64

var f1 MyFloat64
var f float64
f1 = 0.2
f = 0.1
fmt.Println(float64(f1) + f)
0.30000000000000004

Ce n'est qu'en convertissant explicitement MyFloat64 en type float64 que l'addition peut être effectuée. Une autre condition préalable à la conversion de type est que le type converti doit pouvoir être représenté par le type cible (Representability). Par exemple, int peut être représenté par le type int64 et peut également être représenté par le type float64, donc une conversion de type explicite peut être effectuée entre eux. Cependant, le type int ne peut pas être représenté par les types string et bool, donc la conversion de type ne peut pas être effectuée.

TIP

Pour plus de détails sur la définition de Representability, veuillez consulter Manuel de référence - Representability.

Même si deux types peuvent se représenter mutuellement, le résultat de la conversion de type n'est pas toujours correct. Voici un exemple :

go
var num1 int8 = 1
var num2 int32 = 512
fmt.Println(int32(num1), int8(num2))
1 0

num1 a été correctement converti en type int32, mais num2 ne l'a pas été. Il s'agit d'un problème typique de débordement numérique. int32 peut représenter des entiers sur 31 bits, tandis que int8 ne peut représenter que des entiers sur 7 bits. Lors de la conversion d'un entier de haute précision en un entier de basse précision, les bits de poids fort sont abandonnés et les bits de poids faible sont conservés. Par conséquent, le résultat de conversion de num1 est 0. Dans la conversion de type numérique, il est généralement recommandé de convertir du petit vers le grand, et non du grand vers le petit.

Lors de l'utilisation de la conversion de type, il faut éviter les ambiguïtés pour certains types. Voici des exemples :

go
*Point(p) // équivalent à *(Point(p))
(*Point)(p)  // convertit p en type *Point
<-chan int(c)    // équivalent à <-(chan int(c))
(<-chan int)(c)  // convertit c en type <-chan int
(func())(x)      // convertit x en type func()
(func() int)(x)  // convertit x en type func() int

Assertion de type

L'assertion de type est généralement utilisée pour déterminer si une variable de type interface appartient à un certain type. Voici un exemple :

go
var b int = 1
var a interface{} = b
if intVal, ok := a.(int); ok {
   fmt.Println(intVal)
} else {
   fmt.Println("error type")
}
1

Comme interface{} est un type d'interface vide, le type d'interface vide peut représenter tous les types, mais le type int ne peut pas représenter le type interface{}, donc la conversion de type ne peut pas être utilisée. L'assertion de type permet de déterminer si le type sous-jacent est le type souhaité. L'instruction d'assertion de type a deux valeurs de retour : l'une est la valeur après conversion de type, l'autre est la valeur booléenne du résultat de conversion.

Jugement de type

En Go, l'instruction switch prend également en charge une écriture spéciale. Grâce à cette écriture, un traitement logique différent peut être effectué en fonction de différents case. La condition préalable à l'utilisation est que le paramètre d'entrée doit être de type interface. Voici un exemple :

go
var a interface{} = 2
switch a.(type) {
    case int: fmt.Println("int")
    case float64: fmt.Println("float")
    case string: fmt.Println("string")
}
int

TIP

Grâce aux opérations fournies par le package unsafe, il est possible de contourner le système de types de Go et de réaliser des opérations de conversion de type qui ne pouvaient pas être compilées à l'origine.

Golang by www.golangdev.cn edit