CodeGym /Cursos /JAVA 25 SELF /Relação entre polimorfismo e classes abstratas

Relação entre polimorfismo e classes abstratas

JAVA 25 SELF
Nível 18 , Lição 4
Disponível

1. Classes e métodos abstratos

Às vezes, na vida (e na programação), queremos dizer: “Bem, eu não sei exatamente como isso é feito, mas tenho certeza de que isso deve existir!” Por exemplo, todos os animais devem conseguir emitir um som, mas qual som — depende do animal específico. Para esses casos em Java existem os classes abstratas e os métodos abstratos.

Classe abstrata — é uma classe que não pode ser instanciada diretamente (não dá para escrever new Animal() se Animal for abstrata), mas da qual se pode herdar. Essa classe pode conter tanto métodos comuns (implementados) quanto abstratos — isto é, declarados, mas não implementados.

Método abstrato — é um método sem corpo. Ele é declarado com a palavra-chave abstract e deve obrigatoriamente ser implementado nas subclasses (a menos que a subclasse também seja abstrata).

Exemplo do mundo real

Suponha que temos um aplicativo para um zoológico. Queremos que todos os animais tenham o método makeSound(), mas não sabemos qual som exatamente eles emitem. Então fazemos uma classe abstrata:

public abstract class Animal {
    public abstract void makeSound(); // Método abstrato
}

E os animais concretos implementam esse método à sua maneira:

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Au-au!");
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Miau!");
    }
}

Agora, se alguém tentar criar new Animal(), o compilador vai dizer: “Desculpe, mas animais abstratos não existem na natureza!” Isso é útil: você garante que no programa existirão apenas animais concretos com comportamentos concretos.

2. Polimorfismo por meio da abstração

Abstração — é destacar a interface comum para um grupo de objetos. A classe abstrata justamente define essa interface comum: ela especifica quais métodos devem ser obrigatoriamente implementados por todas as subclasses.

Polimorfismo e abstração trabalham em conjunto: a classe abstrata garante que todos os herdeiros tenham os métodos necessários, e o polimorfismo permite chamar esses métodos por meio de uma referência ao tipo base.

Exemplo: criando um zoológico

Vamos montar um pequeno zoológico. Temos a classe abstrata Animal e algumas de suas subclasses:

public abstract class Animal {
    public abstract void makeSound();
}
public class Cow extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Muuu!");
    }
}
public class Duck extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Quá-quá!");
    }
}

Agora podemos criar um array de animais:

Animal[] zoo = {
    new Dog(),
    new Cat(),
    new Cow(),
    new Duck()
};

for (Animal animal : zoo) {
    animal.makeSound(); // Para cada animal será chamado o método "correto"
}

Cada objeto no array é um animal concreto, mas para o código ele é apenas um Animal. Graças ao polimorfismo e à abstração, podemos ter certeza de que cada objeto tem o método makeSound(), e ele funcionará corretamente.

3. Usando classes abstratas para polimorfismo

Vamos considerar um exemplo mais prático. Imagine que estamos desenvolvendo um aplicativo para gerenciamento de funcionários da empresa. Temos diferentes tipos de funcionários: gerentes, desenvolvedores, testadores. Todos têm um método comum work(), mas o executam de forma diferente.

Classe abstrata Employee

public abstract class Employee {
    protected String name;

    public Employee(String name) {
        this.name = name;
    }

    public abstract void work();
}

Subclasses concretas

public class Manager extends Employee {
    public Manager(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " lidera a equipe.");
    }
}

public class Developer extends Employee {
    public Developer(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " escreve código.");
    }
}

public class Tester extends Employee {
    public Tester(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " testa o aplicativo.");
    }
}

Usando polimorfismo

Agora podemos criar um array de funcionários e chamar para cada um o método work():

Employee[] employees = {
    new Manager("Anna"),
    new Developer("Ivan"),
    new Tester("Mariya")
};

for (Employee e : employees) {
    e.work();
}

Resultado:

Anna lidera a equipe.
Ivan escreve código.
Mariya testa o aplicativo.

Observe: não sabemos (e nem queremos saber!) no loop qual é exatamente o tipo do funcionário. Simplesmente chamamos work(), e cada objeto faz o seu trabalho.

4. Nuances úteis

Garantia de implementação de métodos
A classe abstrata obriga todas as subclasses a implementar os métodos necessários. Se você esquecer de implementar um método abstrato na subclasse, o compilador vai te advertir: “Você precisa fazer isso!”

Interface universal
O código que trabalha com um array ou lista do tipo abstrato (Employee[], List<Animal>) pode ser totalmente universal. Você pode adicionar novas subclasses — e não será necessário mudar o código principal.

Proteção contra “objetos aleatórios”
Como uma classe abstrata não pode ser instanciada diretamente, ninguém conseguirá acidentalmente criar um objeto de tipo “desconhecido” que não implemente os métodos necessários.

Teoria e sintaxe: como declarar uma classe e um método abstratos

  • Uma classe abstrata é declarada com a palavra-chave abstract antes de class.
  • Um método abstrato é declarado com a palavra-chave abstract e não tem corpo (apenas ponto e vírgula).
  • Uma classe com pelo menos um método abstrato deve ser abstrata.
  • Uma classe que herda de uma classe abstrata deve implementar todos os seus métodos abstratos, ou ela própria deve ser abstrata.

Esquema

public abstract class Animal {
    public abstract void makeSound();
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Miau!");
    }
}

5. Erros comuns ao trabalhar com classes abstratas

Erro nº 1: tentar criar um objeto de uma classe abstrata.
Código como new Animal() não será compilado. Classes abstratas — são como um manual de montagem de móveis sem as peças: enquanto não existir uma subclasse concreta, não é possível montar o objeto.

Erro nº 2: esqueceu de implementar o método abstrato na subclasse.
Se você declarou um método abstrato mas não o implementou no herdeiro (e não tornou a classe abstrata também), o compilador ficará descontente e mostrará um erro.

Erro nº 3: esqueceu dos modificadores de acesso.
Um método sobrescrito não pode ter um modificador de acesso mais restritivo do que no tipo base. Por exemplo, se o método abstrato era public, então a implementação também deve ser public (e não protected ou private).

Erro nº 4: tentar usar um método abstrato com corpo.
abstract não pode ter corpo; caso contrário, o compilador vai revirar os olhos e dizer: “Decida-se — ou abstração, ou implementação!”

Erro nº 5: polimorfismo não funciona para métodos estáticos.
O polimorfismo funciona apenas para métodos não estáticos. Métodos estáticos não são sobrescritos — eles são ocultados; portanto, o comportamento na chamada depende do tipo da variável, e não do objeto real.

1
Pesquisa/teste
Polimorfismo e sobrecarga, nível 18, lição 4
Indisponível
Polimorfismo e sobrecarga
Polimorfismo e sobrecarga
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION