Swift e Objective-C

Sobrecarga vs. sobrescrita: diferenciando em Swift

Sobrecarga e sobrescrita. Dois conceitos com nomes tão parecidos que dá vontade de perguntar por que precisavam ser assim.

4 min de leitura
Imagem de capa de Sobrecarga vs. sobrescrita: diferenciando em Swift

Sobrecarga e sobrescrita. Dois conceitos com nomes tão parecidos que dá vontade de perguntar por que precisavam ser assim.

São temas frequentes em provas e entrevistas, e os nomes parecidos são os principais responsáveis pela confusão. Os mecanismos são completamente diferentes: uma cria várias funções com o mesmo nome, enquanto a outra redefine uma função herdada.

O momento da decisão também é diferente: uma ocorre em tempo de compilação e a outra, em tempo de execução. Essa diferença leva à discussão de desempenho entre despacho estático e despacho dinâmico.

Neste artigo, vamos separar claramente os dois conceitos com exemplos em Swift.

Este é o resumo dos pontos principais.

  1. Sobrecarga: Definir várias funções com o mesmo nome e parâmetros diferentes. Não depende de herança
  2. Sobrescrita: A classe filha redefine o método da classe pai. Exige herança
  3. A sobrecarga define a função em tempo de compilação; a sobrescrita, em tempo de execução
  4. A sobrescrita é o mecanismo central do polimorfismo

Sobrecarga: várias funções com o mesmo nome

Sobrecarga significa definir várias funções com o mesmo nome, mas com configurações diferentes de parâmetros: quantidade, tipos ou rótulos.

func area(radius: Double) -> Double { .pi * radius * radius }
func area(width: Double, height: Double) -> Double { width * height }
func area(side: Double) -> Double { side * side }

O compilador determina a função com base nos argumentos passados no ponto de chamada. No momento em que você escreve area(radius: 3), a primeira função já está definida. Isso acontece antes da execução.

Pense em C, que não tinha sobrecarga, para perceber sua utilidade. A função de valor absoluto tinha nomes diferentes por tipo: abs, labs e fabs. A sobrecarga permite que conceitos iguais usem o mesmo nome.

Swift usa sobrecarga em toda a biblioteca padrão, em funções como print(:) e min(:_:). A sobrecarga de operadores também permite que um único + trate adição de inteiros, concatenação de strings e combinação de arrays.

Um cuidado: é melhor evitar sobrecargas que diferem apenas pelo tipo de retorno. O Swift permite isso sintaticamente em alguns casos, mas, sem especificar o tipo no ponto de chamada, ocorre um erro de ambiguidade e o código fica confuso.


Sobrescrita: redefinindo o que foi herdado

Sobrescrita é quando uma classe filha redefine, com a mesma assinatura, um método herdado da classe pai. A relação de herança é obrigatória.

class Animal {
    func speak() { print("...") }
}
class Dog: Animal {
    override func speak() { print("Au au") }
}
class Cat: Animal {
    override func speak() { print("Miau") }
}

Este é o ponto principal.

let animals: [Animal] = [Dog(), Cat()]
for animal in animals {
    animal.speak() // Au au, miau
}

O tipo da variável é Animal, mas o método executado é o da instância real. Qual speak será chamado é decidido em tempo de execução, observando o objeto concreto. Isso é despacho dinâmico, o mecanismo que faz o polimorfismo funcionar.

Também vale destacar que o Swift exige a palavra-chave override. O compilador detecta quando você cria por engano um método com a mesma assinatura do método pai ou quando uma falha de digitação impede a sobrescrita. Por outro lado, adicionar final no pai permite proibir sobrescritas.

Mesmo que o tipo da variável seja Animal, o método executado é o da instância real
Mesmo que o tipo da variável seja Animal, o método executado é o da instância real

Comparação rápida em uma tabela

Categoria Sobrecarga Sobrescrita
Definição Mesmo nome, parâmetros diferentes Redefine a mesma assinatura
Herança Não é necessária Obrigatória
Momento da decisão Tempo de compilação (estático) Tempo de execução (dinâmico)
Objetivo Consistência de nomes, conveniência da API Polimorfismo, substituição de comportamento
Palavra-chave do Swift Nenhuma override (obrigatória); proibida com final

A diferença no momento da decisão também se relaciona ao desempenho. Uma chamada definida em tempo de compilação (despacho estático) pode saltar diretamente, enquanto uma decisão em tempo de execução (despacho dinâmico) precisa passar pela tabela de métodos do tipo real. No Swift, usar final ou private dá ao compilador a oportunidade de transformar despacho dinâmico em estático e otimizar.

A sobrecarga é decidida em tempo de compilação; a sobrescrita, em tempo de execução
A sobrecarga é decidida em tempo de compilação; a sobrescrita, em tempo de execução

Uma forma de não confundir

Lembre-se do significado literal das palavras em inglês.

Overload = sobrecarregar. É como “carregar” várias funções sobre o mesmo nome. Um nome, várias funcionalidades.

Override = ignorar e passar por cima. É “sobrescrever” a definição do pai. Uma definição, conteúdo substituído.

Uma resposta de entrevista pode começar assim.

“Sobrecarga é ter várias funções com o mesmo nome e parâmetros diferentes, e a decisão ocorre em tempo de compilação. Sobrescrita é redefinir um método herdado, com a decisão ocorrendo em tempo de execução com base no tipo real. É a sobrescrita que cria o polimorfismo.”


Resumo

  • Sobrecarga: definir várias funções com o mesmo nome e configurações de parâmetros diferentes. Não exige herança
  • Sobrescrita: a classe filha redefine o método do pai com a mesma assinatura. Herança obrigatória
  • A sobrecarga define o destino da chamada em tempo de compilação (estático); a sobrescrita, em tempo de execução (dinâmico)
  • O mecanismo central do polimorfismo é a sobrescrita e o despacho dinâmico
  • Swift evita erros ao exigir a palavra-chave override, e final pode proibir a sobrescrita
  • Forma de lembrar: Overload sobrecarrega um nome; Override sobrescreve uma definição

Continue lendo