Java Essencial: Métodos, parâmetros, retorno e sobrecarga

Capítulo 9

Tempo estimado de leitura: 9 minutos

+ Exercício

O que é um método e por que ele importa

Métodos são blocos de código nomeados que executam uma tarefa específica. Eles ajudam a organizar o programa, reduzir repetição e tornar o código mais legível e testável. Em Java, um método pertence a uma classe e pode ser chamado para executar uma ação (por exemplo, validar um texto) ou produzir um resultado (por exemplo, calcular um valor).

Estrutura básica de um método

Um método é definido por modificadores (opcionais), tipo de retorno, nome, lista de parâmetros e corpo:

modificadores tipoRetorno nomeDoMetodo(tipo1 p1, tipo2 p2) { ... }

Exemplo simples de método que retorna um valor:

static int soma(int a, int b) {    return a + b;}

Assinatura do método: o que identifica um método

A assinatura de um método em Java é composta por: nomeDoMetodo + tipos dos parâmetros (ordem incluída). A assinatura não inclui o tipo de retorno.

Isso é importante para entender sobrecarga: dois métodos podem ter o mesmo nome, desde que tenham listas de parâmetros diferentes.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

static int soma(int a, int b) { return a + b; }static int soma(int a, int b, int c) { return a + b + c; }

Parâmetros e argumentos: entrada de dados

Parâmetros são as variáveis declaradas na definição do método. Argumentos são os valores passados na chamada do método.

static int dobro(int x) { // x é parâmetro    return x * 2;}public static void main(String[] args) {    int r = dobro(21); // 21 é argumento}

Passagem por valor (inclusive para referências)

Em Java, a passagem é sempre por valor. Para tipos primitivos, o valor é copiado. Para tipos por referência, o que é copiado é o valor da referência (o “endereço lógico”), não o objeto em si. Isso afeta como você projeta APIs: um método pode alterar o estado de um objeto recebido, mas não consegue “trocar” a referência do chamador para apontar para outro objeto.

Retorno: produzindo resultados com return

O tipo de retorno define o que o método entrega ao chamador. Se o método não deve produzir um valor, usa-se void.

Métodos com retorno

static double media(double a, double b) {    return (a + b) / 2.0;}

Regras práticas:

  • Todo caminho de execução deve retornar um valor compatível com o tipo declarado.
  • return encerra a execução do método imediatamente.

Método void

Métodos void são usados para executar ações (efeitos), como imprimir, registrar, preencher estruturas, etc.

static void imprimirLinha(String texto) {    System.out.println(texto);}

Mesmo em void, você pode usar return; para sair cedo:

static void validarEImprimir(String texto) {    if (texto == null) return;    System.out.println(texto.trim());}

Escopo de variáveis: onde uma variável existe

Escopo é a região do código onde uma variável pode ser acessada. Em métodos, os casos mais comuns são:

  • Parâmetros: visíveis dentro do método.
  • Variáveis locais: declaradas dentro do método ou bloco ({}), existem apenas ali.
  • Variáveis de bloco: declaradas dentro de if, for, etc., não existem fora do bloco.
static int exemploEscopo(int x) {    int y = x + 1; // y é local do método    if (y > 10) {        int z = y * 2; // z só existe dentro deste bloco        return z;    }    // z não existe aqui    return y;}

Sombreamento (shadowing) e legibilidade

Evite declarar variáveis locais com o mesmo nome de parâmetros, pois isso confunde a leitura:

static int ruim(int valor) {    int valor = 10; // não compila: variável já definida no escopo}

Mesmo quando compila (por exemplo, campos vs locais), prefira nomes distintos para reduzir ambiguidades.

Organização: utilitários com métodos static

Uma forma comum de organizar funções reutilizáveis é criar classes utilitárias com métodos static. Isso é útil para validação, cálculos e formatação, especialmente em projetos pequenos.

Boas práticas para utilitários:

  • Nome da classe no plural ou descritivo: Validacoes, Calculos, Formatacao.
  • Construtor privado para evitar instanciação.
  • Métodos pequenos e focados.
final class Validacoes {    private Validacoes() {}    static boolean isBlank(String s) {        return s == null || s.trim().isEmpty();    }}

Sobrecarga (overload): mesmo nome, parâmetros diferentes

Sobrecarga permite criar múltiplas versões de um método com o mesmo nome, variando a lista de parâmetros (quantidade e/ou tipos). Isso melhora a ergonomia da API, oferecendo atalhos e defaults.

Exemplo: validação com mensagens diferentes

final class Validacoes {    private Validacoes() {}    static void exigirNaoBlank(String s) {        exigirNaoBlank(s, "Texto obrigatório");    }    static void exigirNaoBlank(String s, String mensagem) {        if (s == null || s.trim().isEmpty()) {            throw new IllegalArgumentException(mensagem);        }    }}

Note que o tipo de retorno não diferencia sobrecarga. Isto não é permitido:

// NÃO permitido: mesma assinatura (nome + tipos de parâmetros)static int f(int x) { return x; }static double f(int x) { return x; }

Resolução de sobrecarga: atenção a tipos numéricos

Quando há várias opções, o compilador escolhe a mais específica. Misturar int, long, double pode gerar chamadas ambíguas ou conversões inesperadas. Uma regra prática: mantenha assinaturas claras e evite sobrecargas que diferem apenas por “pequenas” promoções numéricas, a menos que seja realmente necessário.

Varargs: parâmetros variáveis

varargs permite receber zero ou mais argumentos de um mesmo tipo, como um array “implícito”. A sintaxe usa ... e deve ser o último parâmetro.

static int soma(int... valores) {    int total = 0;    for (int v : valores) total += v;    return total;}

Chamadas possíveis:

int a = soma();         // 0int b = soma(1);        // 1int c = soma(1, 2, 3); // 6

Você também pode passar um array explicitamente:

int[] xs = {2, 4, 6};int r = soma(xs);

Varargs e sobrecarga: cuidado com ambiguidades

Evite combinar varargs com sobrecargas muito parecidas, pois pode ficar confuso para o compilador e para quem lê. Prefira assinaturas bem distintas.

Noções de imutabilidade ao projetar APIs simples

Imutabilidade significa que um objeto não muda após ser criado. Em APIs simples, isso traz previsibilidade e reduz efeitos colaterais. Algumas práticas úteis:

  • Preferir retornar novos valores em vez de modificar entradas.
  • Evitar expor estruturas mutáveis diretamente (por exemplo, não retornar arrays internos sem cópia).
  • Tratar Strings como imutáveis: métodos de formatação devem retornar uma nova String.

Exemplo: em vez de “limpar” um texto recebido, retorne uma versão normalizada:

final class Formatacao {    private Formatacao() {}    static String normalizarEspacos(String s) {        if (s == null) return null;        String t = s.trim();        return t.replaceAll("\s+", " ");    }}

Esse estilo facilita testes e evita surpresas: o chamador decide o que fazer com o retorno.

Projeto prático: construir uma mini-biblioteca incremental

A seguir, você vai construir três classes utilitárias (Validacoes, Calculos, Formatacao) e chamá-las a partir do main. A ideia é evoluir aos poucos, adicionando métodos, sobrecargas e varargs.

Passo 1: criar Validacoes com métodos pequenos

Comece com validações comuns de entrada.

final class Validacoes {    private Validacoes() {}    static boolean isBlank(String s) {        return s == null || s.trim().isEmpty();    }    static void exigirNaoNull(Object obj, String nome) {        if (obj == null) {            throw new IllegalArgumentException(nome + " não pode ser null");        }    }    static void exigirNaoBlank(String s, String nome) {        if (isBlank(s)) {            throw new IllegalArgumentException(nome + " não pode ser vazio");        }    }}

Observe o uso de parâmetros para tornar a mensagem reutilizável e o retorno void para um método que “impõe regra” (ou passa, ou lança exceção).

Passo 2: adicionar sobrecarga para ergonomia

Agora crie uma versão mais simples que usa uma mensagem padrão.

final class Validacoes {    private Validacoes() {}    static boolean isBlank(String s) {        return s == null || s.trim().isEmpty();    }    static void exigirNaoBlank(String s) {        exigirNaoBlank(s, "texto");    }    static void exigirNaoBlank(String s, String nome) {        if (isBlank(s)) {            throw new IllegalArgumentException(nome + " não pode ser vazio");        }    }}

Repare que a sobrecarga “menor” delega para a “maior”, centralizando a regra em um único lugar.

Passo 3: criar Calculos com retorno e varargs

Inclua cálculos simples e demonstre varargs.

final class Calculos {    private Calculos() {}    static int max(int a, int b) {        return (a >= b) ? a : b;    }    static double media(double... valores) {        if (valores == null || valores.length == 0) {            throw new IllegalArgumentException("Informe ao menos um valor");        }        double soma = 0.0;        for (double v : valores) soma += v;        return soma / valores.length;    }    static int somar(int... valores) {        int total = 0;        for (int v : valores) total += v;        return total;    }}

Aqui você vê: retorno (int, double), validação de entrada e varargs para uma API mais flexível.

Passo 4: criar Formatacao com foco em imutabilidade

Formatação deve retornar novas Strings, sem alterar o argumento original.

final class Formatacao {    private Formatacao() {}    static String capitalizar(String s) {        if (s == null) return null;        String t = s.trim();        if (t.isEmpty()) return t;        String primeira = t.substring(0, 1).toUpperCase();        String resto = t.substring(1).toLowerCase();        return primeira + resto;    }    static String juntarComVirgula(String... itens) {        if (itens == null || itens.length == 0) return "";        StringBuilder sb = new StringBuilder();        for (int i = 0; i < itens.length; i++) {            if (i > 0) sb.append(", ");            sb.append(itens[i]);        }        return sb.toString();    }}

StringBuilder é útil para montar Strings de forma eficiente. O método retorna uma nova String pronta.

Passo 5: integrar tudo no main (chamadas e testes rápidos)

Crie uma classe de aplicação e chame os utilitários. Use entradas variadas para observar retorno, exceções e sobrecargas.

public class App {    public static void main(String[] args) {        String nome = "  aNa  ";        Validacoes.exigirNaoBlank(nome, "nome");        String nomeFmt = Formatacao.capitalizar(nome);        System.out.println("Nome formatado: " + nomeFmt);        int total = Calculos.somar(10, 20, 30);        System.out.println("Total: " + total);        double m = Calculos.media(7.5, 8.0, 9.0);        System.out.println("Média: " + m);        String lista = Formatacao.juntarComVirgula("validação", "cálculos", "formatação");        System.out.println("Lista: " + lista);        // Exemplo de sobrecarga com default:        Validacoes.exigirNaoBlank("ok");        // Descomente para ver a exceção:        // Validacoes.exigirNaoBlank("   ", "apelido");    }}

Checklist de design ao criar métodos

DecisãoPergunta práticaDica
NomeO nome descreve ação/resultado?Use verbos para ações (validar, exigir) e substantivos para resultados (media, max).
RetornoPreciso devolver algo ao chamador?Se sim, retorne o valor; se não, use void e seja explícito sobre efeitos.
ParâmetrosQuais dados são realmente necessários?Evite muitos parâmetros; prefira sobrecargas ou agrupar em um objeto quando crescer.
EscopoEssa variável precisa existir fora do bloco?Declare o mais próximo possível do uso.
SobrecargaAjuda a API ou confunde?Use para defaults e conveniência; evite assinaturas ambíguas.
ImutabilidadeMeu método altera entradas?Prefira retornar novos valores e documente quando houver mutação.

Agora responda o exercício sobre o conteúdo:

Ao criar métodos utilitários para formatação de texto, qual prática está mais alinhada com a ideia de imutabilidade e previsibilidade da API?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Strings são tratadas como imutáveis; por isso, métodos de formatação devem produzir e retornar um novo valor. Esse estilo reduz efeitos colaterais e torna o comportamento mais previsível e testável.

Próximo capitúlo

Java Essencial: Classes e objetos na prática (estado, comportamento e encapsulamento)

Arrow Right Icon
Capa do Ebook gratuito Java Essencial: Fundamentos da Linguagem e do Ecossistema (JDK, IDE, Maven)
50%

Java Essencial: Fundamentos da Linguagem e do Ecossistema (JDK, IDE, Maven)

Novo curso

18 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.