Polimorfismo

Ao estudarmos Herança, compreendemos que uma subclasse herda atributos e métodos da superclasse. Há, no entanto, situações em que as subclasses precisarão adaptar os métodos da superclasse para que façam sentido às suas demandas locais. Na orientação a objetos, essa flexibilidade é chamada de polimorfismo.

Em TypeScript, isso é frequentemente feito através da sobrescrita (overriding) de métodos. Vamos acompanhar:

✍🏼 Polimorfismo de sobrescrita (overriding)

export class Pessoa{
    private _nome: string;
    
    constructor(nome: string){
        this._nome = nome;
    }
    
    get nome(): string{
        return this._nome;
    }
}
export class Estudante extends Pessoa{
    constructor(nome: string){
        super(nome);
    }
    get nome(): string{
        return `O nome do estudante é ${super.nome}`; 
    }
}
let joao:Pessoa = new Pessoa("João");
let maria:Estudante = new Estudante("Maria");

console.log(joao.nome); // João
console.log(maria.nome); // O nome do estudante é Maria

No exemplo acima, temos uma classe Pessoa com um atributo privado _nome e um método get nome() para acessá-lo. A classe Estudante estende Pessoa e sobrescreve o método get nome() para incluir uma personalização na saída.

Quando maria.nome é chamado, o método sobrescrito na classe Estudante é utilizado, resultando na mensagem personalizada. Esse comportamento é um exemplo clássico de polimorfismo, em que o mesmo método (nome) se comporta de maneiras diferentes dependendo da classe que o invoca.

📦 Polimorfismo com sobrecarga de métodos

Há, também, um outro tipo de polimorfismo: a sobrecarga de métodos. Essa estratégia funciona de forma um pouco diferente nas linguagens TypeScript e Java. Para facilitar a compreensão e oferecer uma visão geral, começaremos entendendo como isso acontece em Java.

Imagine que um método pode receber uma quantidade variável de parâmetros. Em uma situação, nenhum parâmetro pode ser recebido. Em outra situação, pode receber um parâmetro. A função terá comportamentos diferentes para cada situação.

Em Java, podemos reescrever o método, numa prática chamada sobrecarga de métodos.

class Exemplo{
    /// 
    public void imprimir(){
        System.out.println("Oi");
    }
    
    public void imprimir(String nome){
        System.out.println("Oi, " + nome);
    }
}
Exemplo e = new Exemplo();
e.imprimir(); // Oi
e.imprimir("João"); // Oi, João

Em TypeScript, no entanto, não podemos fazer a sobrecarga desta forma. Para alcançar um efeito similar, podemos definir que alguns parâmetros do método são opcionais e fazer o tratamento com estruturas condicionais dentro da implementação do método.

export class Exemplo{
    imprimir(nome?: string): void{
        if(!nome) console.log("Oi");
        else console.log("Oi, " + nome);
    }
}