Menu
Coddy logo textTech

Introdução a Generics

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. Generics resolvem esse problema permitindo que você escreva funções e tipos que funcionam com múltiplos tipos, mantendo total segurança de tipos.

Considere encontrar o valor mínimo em um slice. Sem generics, 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 única 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 tanto int quanto 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, garantindo segurança sem sobrecarga em tempo de execução.

Nas próximas lições, exploraremos parâmetros de tipo, restrições e structs genéricas em detalhes.

challenge icon

Desafio

Fácil

Vamos construir um analisador de pontuação que funcione com pontuações de números inteiros e de ponto flutuante usando generics! Você criará uma única função genérica que pode encontrar o valor máximo em um slice, 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 de análise genérica.

    Crie uma função genérica Max[T int | float64](values []T) T que encontra e retorna o valor máximo em um slice. A função deve funcionar com os tipos int e float64 através do parâmetro de tipo T.

    Também crie uma função genérica Sum[T int | float64](values []T) T que calcula e retorna a soma de todos os valores em um slice.

  • main.go: Leia a entrada e demonstre suas funções genéricas com diferentes tipos.

    Leia um indicador de tipo (int ou float), depois leia uma contagem seguida por essa quantidade de valores. Analise os valores de acordo com o tipo, chame tanto Max quanto Sum com o tipo apropriado e imprima os resultados.

    Para entrada de inteiros, imprima:

    Max (int): [value]
    Sum (int): [value]

    Para entrada de float, imprima com uma casa decimal:

    Max (float): [value]
    Sum (float): [value]

As seguintes entradas serão fornecidas:

  • Linha 1: Indicador de tipo (int ou float)
  • Linha 2: Contagem de valores (inteiro)
  • Linhas seguintes: Um valor por linha

Por exemplo, dado:

int
4
15
8
23
11

Sua saída deve ser:

Max (int): 23
Sum (int): 57

E dado:

float
3
4.5
9.2
6.8

Sua saída deve ser:

Max (float): 9.2
Sum (float): 20.5

Observe 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 com segurança de tipo para cada uso, enquanto você escreve a lógica apenas uma vez.

Folha de consulta

Generics permitem que você escreva código reutilizável que funciona com múltiplos tipos, mantendo a segurança de tipos. Antes do Go 1.18, você precisava de funções separadas para cada tipo ou usava a interface vazia com asserções de tipo.

Parâmetros de tipo são declarados em colchetes e especificam quais tipos uma função genérica pode aceitar:

func Min[T int | float64](values []T) T {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

A sintaxe [T int | float64] declara um parâmetro de tipo T que pode ser int ou float64. O operador pipe | lista os tipos permitidos.

Você pode chamar funções genéricas com diferentes tipos:

Min([]int{3, 1, 2})           // Funciona com int
Min([]float64{3.5, 1.2})      // Funciona com float64

O compilador realiza a verificação de tipos em tempo de compilação, fornecendo segurança sem sobrecarga em tempo de execução.

Experimente você mesmo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Ler o indicador de tipo
	typeStr, _ := reader.ReadString('\n')
	typeStr = strings.TrimSpace(typeStr)

	// Ler a 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 o 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 o slice de float64
		// TODO: Imprimir resultados com uma casa decimal no formato "Max (float): [value]" e "Sum (float): [value]"
	}
}
quiz iconTeste seus conhecimentos

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