CodeGym /Blogue Java /Random-PT /Por que precisamos de interfaces em Java
John Squirrels
Nível 41
San Francisco

Por que precisamos de interfaces em Java

Publicado no grupo Random-PT
Oi! Hoje vamos falar sobre um conceito importante em Java: interfaces. A palavra provavelmente é familiar para você. Por exemplo, a maioria dos programas e jogos de computador possui interfaces. Em um sentido amplo, uma interface é uma espécie de 'controle remoto' que conecta duas partes em interação. Um exemplo simples de interface na vida cotidiana é o controle remoto de uma TV. Ele conecta dois objetos — uma pessoa e uma TV — e realiza diferentes tarefas: aumentar ou diminuir o volume, mudar de canal e ligar ou desligar a TV. Uma parte (a pessoa) precisa acessar a interface (pressione um botão no controle remoto) para fazer com que a segunda parte execute a ação. Por exemplo, para fazer a TV mudar para o próximo canal. Além do mais, o usuário não t precisa saber como a TV está organizada ou como o processo de mudança de canal é implementado internamente. A única coisa a que o usuário tem acesso é a interface. O objetivo principal é obter o resultado desejado. O que isso tem a ver com programação e Java? Tudo :) Criar uma interface é muito semelhante a criar uma classe normal, mas em vez disso, usar a palavraclasse , indicamos a palavra interface . Vejamos a interface Java mais simples, veja como ela funciona e por que precisaríamos dela:

public interface CanSwim {

     public void swim();
}
Criamos uma interface CanSwim . É um pouco como nosso controle remoto, mas com um 'botão': o método swim() . Mas como usamos este controle remoto? Para fazer isso, precisamos implementar um método, ou seja, nosso botão de controle remoto. Para usar uma interface, algumas classes em nosso programa devem implementar seus métodos. Vamos inventar uma classe cujos objetos 'sabem nadar'. Por exemplo, uma classe Duck se encaixa:

public class Duck implements CanSwim {

    public void swim() {
        System.out.println("Duck, swim!");
    }

    public static void main(String[] args) {

        Duck duck = new Duck();
        duck.swim();
    }
}
"O que vemos aqui? A classe Duck é 'associada' à interface CanSwim pela palavra-chave implements . Você deve se lembrar que usamos um mecanismo semelhante para associar duas classes por herança, mas nesse caso usamos a palavra extends. Para Com total clareza, podemos traduzir ' public class Duck implements CanSwim ' literalmente como: 'The public Duck class implements the CanSwim interface'. Isso significa que uma classe associada a uma interface deve implementar todos os seus métodos. Nota: nossa Duckclasse, assim como a CanSwiminterface, tem um swim()método e contém alguma lógica. Este é um requisito obrigatório. Se apenas escrevermospublic class Duck implements CanSwimsem criar um swim()método na Duckclasse, o compilador nos dará um erro: Duck não é abstrato e não substitui o método abstrato swim() em CanSwim Por quê? Por que isso acontece? Se explicarmos o erro usando o exemplo da TV, seria como entregar a alguém um controle remoto de TV com um botão 'trocar de canal' que não pode mudar de canal. Você pode pressionar o botão o quanto quiser, mas não funcionará. O controle remoto não muda de canal sozinho: ele apenas envia um sinal para a TV, que implementa o complexo processo de mudança de canal. E assim é com o nosso pato: ele deve saber nadar para poder ser chamado pela CanSwiminterface. Se não sabe como, oCanSwiminterface não conecta as duas partes — a pessoa e o programa. A pessoa não poderá usar o swim()método para dar um Duckmergulho dentro do programa. Agora você entende com mais clareza para que servem as interfaces. Uma interface descreve o comportamento que as classes que implementam a interface devem ter. 'Behavior' é uma coleção de métodos. Se queremos criar vários mensageiros, o mais fácil é criar uma Messengerinterface. O que todo mensageiro precisa? Em um nível básico, eles devem ser capazes de receber e enviar mensagens.

public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
Agora podemos simplesmente criar nossas classes messenger que implementam a interface correspondente. O próprio compilador vai nos 'forçar' a implementá-los em nossas classes. Telegrama:

public class Telegram implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Telegram message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Telegram message!");
     }
}
Whatsapp:

public class WhatsApp implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a WhatsApp message!");
    }

     public void getMessage() {
         System.out.println("Reading a WhatsApp message!");
     }
}
Viber:

public class Viber implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Viber message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Viber message!");
     }
}
Que vantagens isso oferece? O mais importante deles é o acoplamento fraco. Imagine que estamos projetando um programa que coletará dados do cliente. A Clientclasse definitivamente precisa de um campo para indicar qual mensageiro específico o cliente está usando. Sem interfaces, isso ficaria estranho:

public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
Criamos três campos, mas um cliente pode ter apenas um messenger. Só não sabemos qual. Portanto, temos que adicionar todas as possibilidades à classe para poder se comunicar com o cliente. Acontece que um ou dois deles sempre serão nulltotalmente desnecessários para o programa. É melhor usar nossa interface:

public class Client {

    private Messenger messenger;
}
Este é um exemplo de acoplamento frouxo! Em vez de especificar uma classe de mensageiro específica na Clientclasse, apenas indicamos que o cliente possui um mensageiro. Qual exatamente será determinado enquanto o programa é executado. Mas por que precisamos de interfaces para isso? Por que eles foram adicionados ao idioma? Essa é uma boa pergunta - e a pergunta certa! Não podemos obter o mesmo resultado usando herança comum? A Messengerclasse como pai e Viber, Telegrame WhatsAppcomo filhos. De fato, isso é possível. Mas há um obstáculo. Como você já sabe, Java não possui herança múltipla. Mas há suporte para várias interfaces. Uma classe pode implementar quantas interfaces você quiser. Imagine que temos uma Smartphoneclasse que tem umAppcampo, que representa um aplicativo instalado no smartphone.

public class Smartphone {

    private App app;
}
Claro, um aplicativo e um mensageiro são semelhantes, mas ainda são coisas diferentes. Pode haver versões móveis e de desktop de um messenger, mas App representa especificamente um aplicativo móvel. O problema é o seguinte — se usássemos herança, não poderíamos adicionar um Telegramobjeto à Smartphoneclasse. Afinal, a Telegramclasse não pode herdar simultaneamente Appe Messenger! E nós já o herdamos Messengere o adicionamos à Clientclasse. Mas a Telegramclasse pode facilmente implementar ambas as interfaces! Da mesma forma, podemos dar à Clientclasse um Telegramobjeto como um Messenger, e podemos entregá-lo à Smartphoneclasse como um App. Veja como você faz isso:

public class Telegram implements Application, Messenger {

    // ...methods
}

public class Client {

    private Messenger messenger;

    public Client() {
        this.messenger = new Telegram();
    }
}


public class Smartphone {

    private Application application;

    public Smartphone() {
        this.application = new Telegram();
    }
}
Agora estamos usando a Telegramclasse como queremos. Em alguns lugares, ele atua como um arquivo App. Em outros lugares, ele atua como um arquivo Messenger. Você com certeza já percebeu que os métodos de interface estão sempre 'vazios', ou seja, não possuem implementação. A razão para isso é simples: a interface descreve o comportamento, mas não o implementa. 'Todos os objetos que implementam a CanSwiminterface devem ser capazes de nadar': é tudo o que a interface nos diz. A maneira específica como peixes, patos e cavalos nadam é uma questão para Fish, DuckeHorseclasses, não a interface. Assim como mudar de canal é tarefa da TV. O controle remoto simplesmente fornece um botão para isso. No entanto, uma adição interessante apareceu no Java 8 — métodos padrão. Por exemplo, sua interface possui 10 métodos. 9 delas possuem diferentes implementações em diferentes classes, mas uma é implementada da mesma forma para todas. Anteriormente, antes do Java 8, os métodos de interface não tinham nenhuma implementação: o compilador dava um erro imediatamente. Agora você pode fazer algo assim:

public interface CanSwim {

   public default void swim() {
       System.out.println("Swim!");
   }

   public void eat();

   public void run();
}
Usando a defaultpalavra-chave, criamos um método de interface com uma implementação padrão. Precisamos fornecer nossa própria implementação para dois outros métodos — eat()e run()— em todas as classes que implementam CanSwim. Não precisamos fazer isso com o swim()método: a implementação será a mesma em todas as classes. A propósito, você já encontrou interfaces em tarefas anteriores, mesmo que não tenha notado :) Aqui está um exemplo vívido: Por que as interfaces são necessárias em Java - 2você trabalhou com as interfaces Liste ! SetMais precisamente, você trabalhou com suas implementações — ArrayList, LinkedList, HashSet, etc. O mesmo diagrama fornece claramente um exemplo em que uma classe implementa várias interfaces ao mesmo tempo. Por exemplo, LinkedListimplementa o ListeDeque(fila de duas pontas). Você está familiarizado com a Mapinterface, ou melhor, com sua HashMapimplementação. A propósito, este diagrama ilustra um recurso: as interfaces podem herdar outras interfaces. A SortedMapinterface herda Map, enquanto Dequeherda Queue. Isso é necessário se você quiser mostrar o relacionamento entre interfaces, onde uma interface é uma versão estendida de outra. Vamos considerar um exemplo com a Queueinterface. Ainda não revisamosQueues, mas é bastante simples e funciona como uma fila comum, ou linha, em uma loja. Você só pode adicionar itens no final da fila e só pode pegá-los desde o início. Em algum momento, os desenvolvedores precisaram de uma versão aprimorada da fila para adicionar e receber itens em ambas as extremidades. Então eles criaram uma Dequeinterface, que é uma fila dupla. Tem todos os métodos de uma fila comum. Afinal, é o pai da fila dupla, mas também adiciona novos métodos.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION