Tipi
Nella sezione precedente sui tipi di dati, sono stati brevemente introdotti tutti i tipi di dati incorporati in Go. Questi tipi di base incorporati sono la base per i tipi personalizzati successivi. Go è un tipico linguaggio di tipi statici. I tipi di tutte le variabili vengono determinati durante la compilazione e non cambieranno più durante l'intero ciclo di vita del programma. Questa sezione introdurrà brevemente il sistema dei tipi di Go e l'uso di base.
Tipizzazione Statica Forte
Go è un linguaggio di tipizzazione statica forte. Statico significa che i tipi di tutte le variabili in Go sono già determinati durante la compilazione e non cambieranno più durante il ciclo di vita del programma. Sebbene la dichiarazione di variabili brevi in Go sia simile alla scrittura di linguaggi dinamici, il tipo di variabile è dedotto automaticamente dal compilatore. La differenza fondamentale è che il suo tipo non cambierà più una volta dedotto. I linguaggi dinamici sono completamente opposti. Pertanto, il codice seguente non può essere compilato, poiché a è una variabile di tipo int e non può essere assegnata una stringa:
func main() {
var a int = 64
a = "64"
fmt.Println(a) // cannot use "64" (untyped string constant) as int value in assignment
}La tipizzazione forte si riferisce al fatto che viene eseguito un controllo rigoroso dei tipi nel programma. Quando si verifica una mancata corrispondenza dei tipi, verrà immediatamente detto al programmatore che non dovrebbe farlo, invece di cercare di dedurre il risultato possibile come nei linguaggi dinamici. Pertanto, il codice seguente non può essere compilato, poiché i due tipi sono diversi e non possono essere eseguite operazioni:
func main() {
fmt.Println(1 + "1") // invalid operation: 1 + "1" (mismatched types untyped int and untyped string)
}Tipo Postfisso
Perché Go mette la dichiarazione del tipo dopo invece che prima? In gran parte ha imparato la lezione dal linguaggio C. Prendiamo un esempio ufficiale per mostrare l'effetto. Questo è un puntatore a funzione:
int (*(*fp)(int (*)(int, int), int))(int, int)Onestamente, senza guardare attentamente, è difficile sapere che tipo sia. In Go, una scrittura simile è la seguente:
f func(func(int,int) int, int) func(int, int) intLa dichiarazione di Go segue sempre il principio che il nome è davanti e il tipo è dietro. Leggendolo da sinistra a destra, all'incirca al primo sguardo si può sapere che questa è una funzione e che il valore di ritorno è func(int,int) int. Quando i tipi diventano sempre più complessi, il tipo postfisso è molto migliore in termini di leggibilità. Go in molti aspetti del design è al servizio della leggibilità.
Dichiarazione dei Tipi
In Go, attraverso la dichiarazione dei tipi, è possibile dichiarare un nuovo tipo con un nome personalizzato. Dichiarare un nuovo tipo richiede solitamente un nome di tipo e un tipo di base. Esempi semplici sono i seguenti:
type MyInt int64Nella dichiarazione del tipo sopra, attraverso la parola chiave type è stato dichiarato un tipo di base int64 chiamato MyInt. In Go, ogni nuovo tipo dichiarato deve avere un tipo di base corrispondente e non è consigliabile che il nome del tipo si ripeta con identificatori incorporati esistenti.
type MyInt int64
type MyFloat64 float64
type MyMap map[string]int
// Può essere compilato, ma non è consigliabile utilizzare, questo sovrascriverà il tipo originale
type int int64I tipi dichiarati attraverso la dichiarazione dei tipi sono tutti nuovi tipi. Tipi diversi non possono essere eseguite operazioni, anche se il tipo di base è lo stesso.
type MyFloat64 float64
var f1 MyFloat64
var f float64
f1 = 0.2
f = 0.1
fmt.Println(f1 + f)invalid operation: f1 + f (mismatched types MyFloat64 and float64)Alias di Tipo
L'alias di tipo è diverso dalla dichiarazione dei tipi. L'alias di tipo è solo un alias, non crea un nuovo tipo. Esempi semplici sono i seguenti:
type Int = intEntrambi sono lo stesso tipo, solo il nome è diverso, quindi naturalmente possono essere eseguite operazioni. Quindi l'esempio seguente può essere compilato:
type Int = int
var a Int = 1
var b int = 2
fmt.Println(a + b)3L'alias di tipo è molto utile per alcuni tipi particolarmente complessi. Ad esempio, ora c'è un tipo map[string]map[string]int. Questa è una map bidimensionale. Ora c'è un parametro di funzione che è di tipo map[string]map[string]int, come segue:
func PrintMyMap(mymap map[string]map[string]int) {
fmt.Println(mymap)
}In questo caso, non è necessario utilizzare la dichiarazione dei tipi, poiché la prima dichiara un nuovo tipo e non può essere utilizzata come parametro della funzione. L'esempio dopo aver utilizzato l'alias di tipo è il seguente:
type TwoDMap = map[string]map[string]int
func PrintMyMap(mymap TwoDMap) {
fmt.Println(mymap)
}Dopo aver utilizzato l'alias di tipo, sembra più conciso.
TIP
Il tipo incorporato any è un alias di tipo di interface{}. Entrambi sono completamente equivalenti, solo il nome è diverso.
Conversione dei Tipi
In Go, esiste solo la conversione esplicita dei tipi, non esiste la conversione implicita dei tipi. Pertanto, variabili di tipi diversi non possono essere eseguite operazioni e non possono essere passate come parametri. Il prerequisito per l'applicazione della conversione dei tipi è conoscere il tipo della variabile da convertire e il tipo di destinazione da convertire. Esempi sono i seguenti:
type MyFloat64 float64
var f1 MyFloat64
var f float64
f1 = 0.2
f = 0.1
fmt.Println(float64(f1) + f)0.30000000000000004Convertendo esplicitamente MyFloat64 in tipo float64, è possibile eseguire l'operazione di addizione. Un altro prerequisito per la conversione dei tipi è: il tipo da convertire deve essere rappresentabile dal tipo di destinazione (Representability). Ad esempio, int può essere rappresentato dal tipo int64 e può anche essere rappresentato dal tipo float64, quindi possono essere eseguite conversioni esplicite tra di loro. Ma il tipo int non può essere rappresentato dai tipi string e bool, quindi non è possibile eseguire la conversione dei tipi.
TIP
Per la definizione di Representability, andare su Riferimento Manuale - Representability per ulteriori dettagli.
Anche se due tipi possono rappresentarsi a vicenda, il risultato della conversione dei tipi non è sempre corretto. Guarda l'esempio seguente:
var num1 int8 = 1
var num2 int32 = 512
fmt.Println(int32(num1), int8(num2))1 0num1 è stato convertito correttamente in tipo int32, ma num2 no. Questo è un tipico problema di overflow numerico. int32 può rappresentare interi a 31 bit, int8 può solo rappresentare interi a 7 bit. Quando gli interi ad alta precisione vengono convertiti in interi a bassa precisione, i bit alti vengono scartati e i bit bassi vengono mantenuti. Pertanto, il risultato della conversione di num1 è 0. Nella conversione dei tipi numerici, si consiglia generalmente di convertire da piccolo a grande, e non è consigliabile convertire da grande a piccolo.
Quando si utilizza la conversione dei tipi, per alcuni tipi è necessario evitare ambiguità. Esempi sono i seguenti:
*Point(p) // equivalente a *(Point(p))
(*Point)(p) // converte p nel tipo *Point
<-chan int(c) // equivalente a <-(chan int(c))
(<-chan int)(c) // converte c nel tipo <-chan int
(func())(x) // converte x nel tipo func()
(func() int)(x) // converte x nel tipo func() intAffermazione di Tipo
L'affermazione di tipo è solitamente utilizzata per determinare se una variabile di tipo interfaccia appartiene a un determinato tipo. Esempio è il seguente:
var b int = 1
var a interface{} = b
if intVal, ok := a.(int); ok {
fmt.Println(intVal)
} else {
fmt.Println("error type")
}1Poiché interface{} è un tipo di interfaccia vuota, il tipo di interfaccia vuota può rappresentare tutti i tipi, ma il tipo int non può rappresentare il tipo interface{}, quindi non è possibile utilizzare la conversione dei tipi. L'affermazione di tipo può determinare se il tipo sottostante è il tipo desiderato. L'affermazione di tipo ha due valori di ritorno: uno è il valore dopo la conversione del tipo, l'altro è il valore booleano del risultato della conversione.
Giudizio di Tipo
In Go, l'istruzione switch supporta anche una scrittura speciale. Attraverso questa scrittura, è possibile eseguire diverse elaborazioni logiche in base a diversi case. Il prerequisito per l'uso è che il parametro di ingresso deve essere di tipo interfaccia. Esempio è il seguente:
var a interface{} = 2
switch a.(type) {
case int: fmt.Println("int")
case float64: fmt.Println("float")
case string: fmt.Println("string")
}intTIP
Attraverso le operazioni fornite dal pacchetto unsafe, è possibile aggirare il sistema dei tipi di Go, rendendo possibili operazioni di conversione dei tipi che originariamente non potevano essere compilate.
