Introdução aos genéricos
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 67 de 107.
Antes do Go 1.18, escrever código reutilizável para diferentes tipos significava duplicar funções ou usar a interface vazia any com asserções de tipo. Os genéricos resolvem esse problema permitindo que você escreva funções e tipos que funcionam com vários tipos, mantendo a segurança de tipos completa.
Considere encontrar o valor mínimo em uma fatia. Sem genéricos, você precisaria de funções separadas para cada tipo:
func MinInt(values []int) int {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}
func MinFloat64(values []float64) float64 {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}Com generics, você escreve a lógica uma vez usando um parâmetro de tipo entre colchetes:
func Min[T int | float64](values []T) T {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}O [T int | float64] declara um parâmetro de tipo T que pode ser int ou float64. Agora você pode chamar Min([]int{3, 1, 2}) ou Min([]float64{3.5, 1.2}) com a mesma função. O compilador verifica os tipos em tempo de compilação, então você obtém segurança sem sobrecarga em tempo de execução.
Nas próximas lições, exploraremos em detalhes os parâmetros de tipo, as restrições e as estruturas genéricas.
Desafio
FácilVamos criar um analisador de pontuações que funcione com pontuações inteiras e de ponto flutuante usando genéricos! Você criará uma única função genérica que pode encontrar o valor máximo em uma fatia, eliminando a necessidade de código duplicado para diferentes tipos numéricos.
Você organizará seu código em dois arquivos:
analyzer.go: Defina sua função genérica de análise.Crie uma função genérica
Max[T int | float64](values []T) Tque encontre e retorne o valor máximo em uma fatia. A função deve funcionar com os tiposintefloat64por meio do parâmetro de tipoT.Crie também uma função genérica
Sum[T int | float64](values []T) Tque calcule e retorne a soma de todos os valores em uma fatia.main.go: Leia a entrada e demonstre suas funções genéricas com diferentes tipos.Leia um indicador de tipo (
intoufloat), depois leia uma quantidade seguida por essa quantidade de valores. Analise os valores de acordo com o tipo, chameMaxeSumcom o tipo apropriado e imprima os resultados.Para uma entrada inteira, imprima:
Max (int): [value] Sum (int): [value]Para uma entrada de ponto flutuante, imprima com uma casa decimal:
Max (float): [value] Sum (float): [value]
As seguintes entradas serão fornecidas:
- Linha 1: Indicador de tipo (
intoufloat) - Linha 2: Quantidade de valores (inteiro)
- Linhas seguintes: Um valor por linha
Por exemplo, considerando:
int
4
15
8
23
11Sua saída deve ser:
Max (int): 23
Sum (int): 57E considerando:
float
3
4.5
9.2
6.8Sua saída deve ser:
Max (float): 9.2
Sum (float): 20.5Observe como as mesmas funções genéricas lidam com dados inteiros e de ponto flutuante: o parâmetro de tipo [T int | float64] permite que o compilador gere código seguro quanto aos tipos para cada uso, enquanto você escreve a lógica apenas uma vez.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Ler indicador de tipo
typeStr, _ := reader.ReadString('\n')
typeStr = strings.TrimSpace(typeStr)
// Ler contagem
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
if typeStr == "int" {
// Ler valores inteiros
values := make([]int, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.Atoi(strings.TrimSpace(line))
}
// TODO: Chamar Max e Sum com slice de inteiros
// TODO: Imprimir resultados no formato "Max (int): [value]" e "Sum (int): [value]"
} else if typeStr == "float" {
// Ler valores float
values := make([]float64, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
}
// TODO: Chamar Max e Sum com slice de float64
// TODO: Imprimir resultados com uma casa decimal no formato "Max (float): [value]" e "Sum (float): [value]"
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP em Go
Arquivos ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker PoolPratique por conta própria: Compilador de Go online