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 tipos 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 genéricos, 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 detalhadamente os parâmetros de tipo, as restrições e as estruturas genéricas.
Desafio
FácilVamos criar um analisador de pontuações que funciona tanto com pontuações inteiras quanto com números de ponto flutuante usando genéricos! Você criará uma única função genérica que encontra 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 encontra e retorna o valor máximo em uma fatia. A função deve funcionar tanto com os tiposintquantofloat64por meio do parâmetro de tipoT.Crie também uma função genérica
Sum[T int | float64](values []T) Tque calcula e retorna 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) e, em seguida, leia uma quantidade seguida desse número de valores. Analise os valores de acordo com o tipo, chameMaxeSumcom o tipo apropriado e imprima os resultados.Para uma entrada de inteiros, imprima:
Max (int): [value] Sum (int): [value]Para uma entrada de números 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, dada a entrada:
int
4
15
8
23
11Sua saída deve ser:
Max (int): 23
Sum (int): 57E dada a entrada:
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)
// Leia o indicador de tipo
typeStr, _ := reader.ReadString('\n')
typeStr = strings.TrimSpace(typeStr)
// Leia a quantidade
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
if typeStr == "int" {
// Leia os valores inteiros
values := make([]int, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.Atoi(strings.TrimSpace(line))
}
// TODO: Chame Max e Sum com o slice de inteiros
// TODO: Imprima os resultados no formato "Max (int): [value]" e "Sum (int): [value]"
} else if typeStr == "float" {
// Leia os valores de ponto flutuante
values := make([]float64, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
}
// TODO: Chame Max e Sum com o slice de float64
// TODO: Imprima os 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