CodeGym /Cursos /JAVA 25 SELF /Implementação múltipla de interfaces

Implementação múltipla de interfaces

JAVA 25 SELF
Nível 20 , Lição 2
Disponível

1. Introdução

Em Java, não é possível herdar várias classes ao mesmo tempo. Isso é feito de propósito para evitar o “diamante da morte” — situação em que duas classes-pai definem o mesmo método e não fica claro qual implementação usar. Já interfaces você pode implementar quantas quiser! Por quê? Porque a interface é apenas um contrato; ela não contém implementação (até o Java 8 — sem implementação; a partir do Java 8 — são possíveis métodos default e static). Portanto, não há confusão com herança de código.

É parecido com a vida real: você pode ser simultaneamente “Motorista”, “Usuário de computador” e “Nadador”. Cada uma dessas “interfaces” descreve determinadas habilidades, mas não obriga você a ser uma cópia de outra pessoa.

Sintaxe da implementação múltipla de interfaces

Em Java, uma classe pode implementar várias interfaces, listando-as separadas por vírgula após a palavra-chave implements. Veja um exemplo básico:

public interface Movable {
    void move(int x, int y);
}

public interface Chargeable {
    void charge();
}

public class Robot implements Movable, Chargeable {
    @Override
    public void move(int x, int y) {
        System.out.println("O robô se move para o ponto (" + x + ", " + y + ")");
    }

    @Override
    public void charge() {
      System.out.println("O robô está carregando.");
    }
}

Neste exemplo, Robot é bastante versátil: ele se move e também carrega. Como na vida: quanto mais você sabe, mais frequentemente é chamado para entrevistas!

2. Para que serve? Exemplos práticos

Exemplo 1. Diferentes “papéis” de um objeto

Imagine que você está projetando um personagem de jogo:

  • Ele pode se mover (Movable)
  • Pode atacar (Attackable)
  • Pode ser salvo em arquivo (Serializable — existe esse interface na biblioteca padrão do Java)
public interface Attackable {
    void attack();
}

public class Hero implements Movable, Attackable, java.io.Serializable {
    @Override
    public void move(int x, int y) {
        System.out.println("O herói se move para uma nova posição.");
    }

    @Override
    public void attack() {
        System.out.println("O herói ataca!");
    }
}

Agora sua classe pode ser usada em diversos contextos: você pode passá-la para métodos que exijam qualquer uma dessas interfaces.

Exemplo 2. Combinando interfaces padrão

Com muita frequência, na biblioteca padrão do Java aparecem as interfaces Comparable (para comparar objetos) e Serializable (para salvar objetos em arquivo ou transmiti-los pela rede). Às vezes, é necessário que o objeto seja as duas coisas:

public class Person implements Comparable<Person>, java.io.Serializable {
    private String name;
    private int age;

    public Person(String name, int age) { this.name = name; this.age = age; }

    @Override
    public int compareTo(Person other) {
        return Integer.compare(this.age, other.age);
    }
}

Agora objetos Person podem ser ordenados (por exemplo, em uma lista) e gravados em arquivo.

3. Características e limitações

Uma implementação por método

Se duas interfaces definirem um método com a mesma assinatura, você precisa implementá-lo apenas uma vez. Exemplo:

public interface A {
    void doSomething();
}
public interface B {
    void doSomething();
}
public class MyClass implements A, B {
    @Override
    public void doSomething() {
        System.out.println("Implementação de doSomething para ambas as interfaces.");
    }
}

O Java não reclamará — o importante é que as assinaturas coincidam. Se os métodos diferirem pela assinatura, eles são considerados métodos distintos — é preciso implementar cada um.

Sem “diamante da morte”

Diferentemente da herança múltipla de classes, ao implementar várias interfaces não surge a situação de herdar duas implementações diferentes do mesmo método. Até o Java 8, não havia implementação em interfaces; e, com o surgimento dos métodos default, se houver conflito, você é obrigado a resolvê-lo explicitamente (mais detalhes na próxima aula).

Sem estado

Interfaces não podem conter campos comuns (apenas constantes — public static final). Portanto, não há confusão com “dois campos pai com o mesmo nome”.

4. Exemplo: implementando várias interfaces em uma única classe

Vamos adicionar novas funcionalidades ao nosso aplicativo de estudo (por exemplo, um zoológico). Suponha que temos animais que podem se mover e emitir som:

public interface Movable {
    void move(int x, int y);
}

public interface Soundable {
    void makeSound();
}

public class Dog implements Movable, Soundable {
    private String name;

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

    @Override
    public void move(int x, int y) {
        System.out.println(name + " corre para (" + x + ", " + y + ")");
    }

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

public class Cat implements Movable, Soundable {
    private String name;

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

    @Override
    public void move(int x, int y) {
        System.out.println(name + " se esgueira para (" + x + ", " + y + ")");
    }

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

Agora podemos escrever um método genérico para trabalhar com qualquer objeto “que se move” ou “que emite som”:

public static void testMovable(Movable m) {
    m.move(10, 20);
}

public static void testSoundable(Soundable s) {
    s.makeSound();
}

public static void main(String[] args) {
    Dog rex = new Dog("Reks");
    Cat murka = new Cat("Murka");

    testMovable(rex);       // Reks corre para (10, 20)
    testSoundable(murka);   // Murka diz: Miau!
}

E, claro, se um objeto implementa ambas as interfaces, ele pode ser passado para um e para outro!

6. Nuances úteis

E se as interfaces entrarem em conflito?

Às vezes acontece de duas interfaces definirem métodos com a mesma assinatura, mas com significados diferentes. Por exemplo, uma interface espera que o método reset() zere as coordenadas, enquanto outra espera que esse mesmo método desligue o dispositivo. Nesse caso, é preciso ter atenção: ainda assim você deve implementar o método apenas uma vez, e ele deve “saber” lidar com ambos os comportamentos (ou pelo menos escolher o que fazer). Na prática, tais situações são raras, mas, se ocorrerem, vale repensar o desenho do sistema.

Exemplo com uma coleção de objetos de diferentes interfaces

Suponha que temos uma lista de objetos que implementam interfaces diferentes. Podemos percorrê-los e chamar os métodos necessários:

Movable[] movables = {
    new Dog("Sharik"),
    new Cat("Barsik"),
    new Robot()
};

for (Movable m : movables) {
    m.move(0, 0);
}

De forma análoga, é possível fazer isso para qualquer interface.

7. Erros comuns ao implementar várias interfaces

Erro nº 1: nem todos os métodos das interfaces foram implementados.
Se a classe declarou que implementa uma interface, mas não implementou pelo menos um de seus métodos — o compilador emitirá um erro imediatamente. Não se esqueça de todos os métodos, mesmo que pareçam “supérfluos”.

Erro nº 2: métodos conflitantes com a mesma assinatura.
Se duas interfaces definem métodos iguais, você deve implementá-los apenas uma vez. Mas se o significado desses métodos for diferente, isso pode levar a confusão e bugs. Nesse caso, é melhor repensar a arquitetura.

Erro nº 3: tentativa de herdar interface com extends em uma classe.
Em uma classe, para implementar uma interface sempre se usa implements, e não extends. Por exemplo:

public class MyClass implements A, B { ... } // correto
public class MyClass extends A, B { ... }    // erro!

Erro nº 4: tentativa de criar um objeto de uma interface.
Interface é um contrato; não é possível instanciá-la diretamente:

Movable m = new Movable(); // erro de compilação

Você só pode criar objetos de classes que implementam a interface.

1
Tarefa
JAVA 25 SELF, nível 20, lição 2
Bloqueado
Tela Digital: Desenhar e Apagar
Tela Digital: Desenhar e Apagar
1
Tarefa
JAVA 25 SELF, nível 20, lição 2
Bloqueado
Perfil do Usuário: Nome e Capacidade de Serialização
Perfil do Usuário: Nome e Capacidade de Serialização
1
Tarefa
JAVA 25 SELF, nível 20, lição 2
Bloqueado
Ação Universal: Botão Único para Tudo
Ação Universal: Botão Único para Tudo
1
Tarefa
JAVA 25 SELF, nível 20, lição 2
Bloqueado
Centro de Mídia: Reprodução e Gravação
Centro de Mídia: Reprodução e Gravação
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION