CodeGym /Cursos /JAVA 25 SELF /Criação de hierarquias de classes, exemplos do mundo real...

Criação de hierarquias de classes, exemplos do mundo real

JAVA 25 SELF
Nível 17 , Lição 3
Disponível

1. O que é uma hierarquia de classes?

Na programação (e não apenas nela), hierarquia é uma estrutura em árvore, em que temos entidades “gerais” no topo da árvore (classes base) e, abaixo, as mais específicas (subclasses). Em Java, a hierarquia de classes é construída com herança: cada subclasse pode, por sua vez, ser pai de outras subclasses e assim por diante.

Analogia:

  • Animal (Animal) — é uma classe geral.
  • Mamífero (Mammal) — é um caso particular de animal.
  • Cachorro (Dog) — é um caso particular de mamífero.
  • Já um “Sharik” específico — é um objeto da classe Dog.

No código, isso fica assim:

class Animal { }
class Mammal extends Animal { }
class Dog extends Mammal { }

A hierarquia de classes permite descrever propriedades e comportamentos comuns “no topo”, e os detalhes “na base”. Isso torna o código mais lógico e evita duplicação.

Esquema da hierarquia

Animal
├── Mammal
│   ├── Dog
│   └── Cat
└── Bird
    └── Sparrow

2. Como construir a hierarquia: lógica e prática

Definindo o que é geral e o que é específico

A regra principal: a classe base deve conter o que é característico de todos os seus descendentes. Tudo o que for único — mova para as subclasses.

Exemplo:

  • Todos os animais podem respirar e comer — logo, os métodos breathe() e eat() devem estar na classe Animal.
  • Apenas aves sabem voar — então o método fly() deve estar na classe Bird, e não em Animal.
  • Apenas cães sabem latir — o método bark() deve estar na classe Dog.

Exemplo: Animais

// Classe base
class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

    void eat() {
        System.out.println(name + " come.");
    }

    void makeSound() {
        System.out.println(name + " emite um som.");
    }
}

// Subclasse: Mamífero
class Mammal extends Animal {
    Mammal(String name) {
        super(name);
    }

    void feedMilk() {
        System.out.println(name + " amamenta os filhotes com leite.");
    }
}

// Subclasse: Cachorro
class Dog extends Mammal {
    Dog(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " late: Au-au!");
    }

    void wagTail() {
        System.out.println(name + " abana o rabo.");
    }
}

// Subclasse: Gato
class Cat extends Mammal {
    Cat(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " mia: Miau!");
    }

    void purr() {
        System.out.println(name + " ronrona.");
    }
}

// Subclasse: Ave
class Bird extends Animal {
    Bird(String name) {
        super(name);
    }

    void fly() {
        System.out.println(name + " voa.");
    }

    @Override
    void makeSound() {
        System.out.println(name + " pia: Piu-piu!");
    }
}

Chamada no main:

public class ZooDemo {
    public static void main(String[] args) {
        Dog sharik = new Dog("Sharik");
        Cat murka = new Cat("Murka");
        Bird sparrow = new Bird("Vorobey");

        sharik.eat();        // Sharik come.
        sharik.makeSound();  // Sharik late: Au-au!
        sharik.feedMilk();   // Sharik amamenta os filhotes com leite.
        sharik.wagTail();    // Sharik abana o rabo.

        murka.eat();         // Murka come.
        murka.makeSound();   // Murka mia: Miau!
        murka.feedMilk();    // Murka amamenta os filhotes com leite.
        murka.purr();        // Murka ronrona.

        sparrow.eat();       // Vorobey come.
        sparrow.makeSound(); // Vorobey pia: Piu-piu!
        sparrow.fly();       // Vorobey voa.
    }
}

Visualização: árvore de classes

Classe Superclasse Características
Animal
Object
name, eat(), makeSound()
Mammal
Animal
feedMilk()
Dog
Mammal
makeSound(), wagTail()
Cat
Mammal
makeSound(), purr()
Bird
Animal
fly(), makeSound()

3. Mais exemplos do mundo real

Figuras geométricas

Hierarquias de classes são excelentes para modelar geometria.

// Classe base
class Shape {
    void draw() {
        System.out.println("Desenhando uma figura.");
    }
}

// Círculo
class Circle extends Shape {
    double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    void draw() {
        System.out.println("Desenhando um círculo de raio " + radius);
    }
}

// Retângulo
class Rectangle extends Shape {
    double width, height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    void draw() {
        System.out.println("Desenhando um retângulo " + width + "x" + height);
    }
}

Uso:

public class ShapeDemo {
    public static void main(String[] args) {
        Shape s1 = new Circle(5);
        Shape s2 = new Rectangle(3, 4);

        s1.draw(); // Desenhando um círculo de raio 5.0
        s2.draw(); // Desenhando um retângulo 3.0x4.0
    }
}

Transportes

class Vehicle {
    void move() {
        System.out.println("O veículo se move.");
    }
}

class Car extends Vehicle {
    @Override
    void move() {
        System.out.println("O carro anda na estrada.");
    }
}

class Bicycle extends Vehicle {
    @Override
    void move() {
        System.out.println("O ciclista pedala.");
    }
}

main:

Vehicle v1 = new Car();
Vehicle v2 = new Bicycle();

v1.move(); // O carro anda na estrada.
v2.move(); // O ciclista pedala.

Usuários

class User {
    String username;
    User(String username) { this.username = username; }
    void login() { System.out.println(username + " fez login no sistema."); }
}

class Admin extends User {
    Admin(String username) { super(username); }
    void deleteUser(String user) {
        System.out.println(username + " excluiu o usuário " + user);
    }
}

class Customer extends User {
    Customer(String username) { super(username); }
    void buy() { System.out.println(username + " realizou uma compra."); }
}

4. Nuances úteis

Não abuse da herança. Herança é uma ferramenta para relações “is-a”. Se você quer dizer “Gato é um Animal”, use herança. Se “Gato contém Cauda”, use composição (has-a).

Ruim:

class Engine { /* ... */ }
class Car extends Engine { /* Carro é um motor? Não, isso é composição! */ }

Bom:

class Car {
    Engine engine; // O carro contém um motor
}

Não crie hierarquias muito profundas. Quanto mais níveis — mais difícil é manter e entender o código. Normalmente, 2–3 níveis são o máximo para a maioria das tarefas.

Não crie hierarquias muito “planas”. Se você tem 20 classes, todas herdando diretamente de uma única base, talvez seja hora de rever a arquitetura.

Herança “por conveniência”. Às vezes dá vontade de “pegar emprestados” métodos ou campos sem pensar na lógica. Isso leva ao caos! Se class A e class B não estão logicamente relacionados, não faça class B extends A apenas por causa de alguns métodos.

Violação do princípio da substituição de Liskov. Se a subclasse não puder ser usada no lugar do pai sem surpresas — a hierarquia foi construída de forma incorreta.

Duplicação de código. Se você se pega pensando “opa, este método está sendo copiado de classe em classe”, talvez seja hora de movê-lo para a classe base.

5. Erros comuns ao construir hierarquias de classes

Erro nº 1: Herança sem a lógica “is-a”.
Se você usa herança apenas para acessar métodos ou campos, e não porque a subclasse realmente “é” o pai, seu código vai virar uma bagunça rapidamente. Por exemplo, “Carro herda Motor” — isso não é is-a, e sim has-a.

Erro nº 2: Ignorar características únicas da subclasse.
Se todas as suas subclasses parecem iguais e não adicionam nada novo, talvez a hierarquia não seja necessária — use uma única classe.

Erro nº 3: Duplicar código nas subclasses.
Se você copia a mesma implementação para várias subclasses, vale a pena movê-la para a classe pai.

Erro nº 4: Hierarquia muito complexa ou profunda.
Hierarquias com muitos níveis são difíceis de manter e testar. Quanto mais simples — melhor!

Erro nº 5: Sobrescrever métodos sem a anotação @Override.
Sem a anotação, é fácil errar na assinatura do método e, então, o método não será sobrescrito, mas considerado um novo. Sempre use @Override!

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION