Производные типы

Именованные типы и псевдонимы

Последнее обновление: 18.02.2024

Оператор type позволяет определять именнованный тип на основе другого. Например:

type mile uint

В данном случае определяется именованный тип mile, который основывается на типе uint. По сути mile представляет тип uint и работа с ним будет производиться также, как и с типом uint. Однако в то же время фактически это новый тип.

Мы можем определять переменные данного типа, работать с ними как с объектами базового типа uint:

package main
import "fmt"

type mile uint

func main() {
	
	var distance mile = 5
	fmt.Println(distance)
	distance += 5
	fmt.Println(distance)
}

Но может возникнуть вопрос, а зачем это нужно, зачем определять именнованный тип, если он все равно ведет себя как тип uint? Рассмотрим следующую ситуацию:

package main
import "fmt"

type mile uint
type kilometer uint

func distanceToEnemy (distance mile){
	
	fmt.Println("расстояние для противника:")
	fmt.Println(distance, "миль")
}

func main() {
	
	var distance mile = 5
	distanceToEnemy(distance)

	
    // var distance1 uint = 5
    // distanceToEnemy(distance1)   // !Ошибка

	// var distance2 kilometer = 5
	// distanceToEnemy(distance2)	// ! ошибка
}

Здесь определены два именнованных типа: mile и kilometer, которые по сути представляют тип uint и которые предназначены для выражения расстояния в милях и километрах соответственно. И также определена функция distanceToEnemy(), которая отображает расстояние в милях до условного противника. В качестве параметра принимает значение mile - именно значение типа mile, а не типа uint. Это позволит нам уменьшить вероятность передачи некорректных данных. То есть передаваемые данные должны быть явным образом определены в программе как значение типа mile, а не типа uint или типа kilometer. Два именнованных типа считаются разными, даже если они основаны на некотором общем типе (как uint в данном случае).

Также именнованные типы позволяют придать типу некоторый дополнительный смысл. Так, использование в коде типа "kilometer" или "mile" позволит указать на предназначение переменной или параметра и будет более описательным, чем просто тип uint.

Еще одна ситуация, где можно применять именнованные типы - это сокращение названия типов в том случае, если они слишком длинные или громоздкие. Например, рассмотрим следующий пример:

package main
 
import "fmt"

func action(n1 int, n2 int, op func(int, int) int){
 
    result := op(n1, n2)
    fmt.Println(result)
}

func add(x int, y int) int {
     
    return x + y
}

func main() {
     
	var myOperation func(int, int) int = add
    action(10, 25, myOperation)     // 35
}

Здесь определена функция action, которая принимает два числа и некоторую другую функцию с типом func(int, int) int - то есть функцию, которая принимает два числа и также возвращает число. В функции main определяется переменная myOperation, которая как раз представляет функцию типа func(int, int) int, получает ссылку на функцию add и передается в вызов action(10, 25, myOperation)

Теперь определим именнованный тип для типа func(int, int) int:

package main
 
import "fmt"
 
type BinaryOp func(int, int) int

func action(n1 int, n2 int, op BinaryOp){
 
    result := op(n1, n2)
    fmt.Println(result)
}

func add(x int, y int) int {
     
    return x + y
}

func main() {
     
	var myOperation BinaryOp = add
    action(10, 35, myOperation)     // 45
}

Теперь тип функции func(int, int) int проецируется на именнованный тип BinaryOp, который представляет бинарную операцию над двумя операндами:

type BinaryOp func(int, int) int

Такое название короче оригинального определения типа и в то же время является более описательным (по крайней мере для меня). Соответственно далее его можно использовать для указания типа параметра:

func action(n1 int, n2 int, op BinaryOp){ ... }

или переменной:

var myOperation BinaryOp = add

Псевдонимы

На именнованные типы похожи псевдонимы. Они также определяются с помощью оператора type, только при присвоении типа применяется операция присваивания:

type псевдоним = имеющийся_тип

Однако псевдоним НЕ определяет нового типа и все псевдонимы одного и того же типа считаются идентичными. Например:

package main
import "fmt"
 
type mile = uint
type kilometer = uint
 
func distanceToEnemy (distance mile){
     
    fmt.Println("расстояние для противника:")
    fmt.Println(distance, "миль")
}
 
func main() {
     
    var distance mile = 5
    distanceToEnemy(distance)

    var distance1 uint = 5
    distanceToEnemy(distance1)   // норм 

    var distance2 kilometer = 5
    distanceToEnemy(distance2)   // норм
}

Здесь для типа uint определяются два псевдонима - mile и kilometer. И несмотря на то, что параметр функции distanceToEnemy определен как параметр типа mile, ему можно передать и значение собственно типа uint, и значение его псевдонима - kilometer.

Обычо псевдонимы применяются для сокращения названий других типов или для определения более описательного имени.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850