Comentários

0%

Não pode faltar

DEFINIÇÃO E USO DE INTERFACES

Jesimar da Silva Arantes

Interfaces Java

O conceito de interfaces em Java é o de sanar a limitação da linguagem em não permitir heranças múltiplas, assim uma interface estabelece um contrato a ser seguido ao desenvolver as classes que implementam as interfaces.

Fonte: Shutterstock.

Deseja ouvir este material?

Áudio disponível no material digital.

Praticar para aprender

Caro estudante, bem-vindo à segunda seção da terceira unidade de estudos sobre Linguagem Orientada a Objetos. Uma vez que estudamos a respeito de herança, às vezes podemos nos perguntar se há a possibilidade de herdarmos mais de uma classe. A linguagem Java não nos permite fazer a herança de mais de uma classe, no entanto, o Java dá suporte a interfaces que são como contratos, que devem ser desenvolvidos pelas classes que os implementam. Ao unir conteúdo de herança, classe abstrata e interface, a linguagem Java consegue oferecer uma grande reusabilidade de código e fechar o assunto de polimorfismo. Nesta seção, você terá a oportunidade de aprender a sintaxe básica por trás do comando for each, que é muito utilizado para se iterar sobre coleções, como vetores e listas, além de como se faz para passar para métodos uma quantidade variada de argumentos e como trabalhar com enumeração em Java. 

De forma a contextualizar sua aprendizagem, imagine que você foi contratado em uma startup. A ideia é que você desenvolva um simulador completo de robô que seja capaz de transportar caixas em uma sala. O seu chefe está muito satisfeito com o que você está desenvolvendo, porém sabe que ainda há muito trabalho a se fazer. Após revisar o seu código, percebeu que ele não estava devidamente documentado, então pediu a você que começasse a documentá-lo utilizando a estrutura do javadoc. Além disso, ele também notou que em seu código havia algumas constantes declaradas como static e final, mas que ele acreditava que ficariam melhores utilizando-se enumeradores. O seu chefe também identificou que o seu código não possuía nenhuma descrição de utilização para quando fosse executado via linha de comando, logo, pediu a você que imprimisse algumas informações, caso requisitadas durante a execução pelo terminal. Por fim, ele deseja que o robô possa receber, por meio da execução por linha de comando, uma lista de comandos ou movimentações possíveis, bem como executar esses movimentos automaticamente; para isso, ele disse a você que é possível utilizar o comando for each e a ideia de argumentos variáveis (varargs) do Java. 

Diante desse desafio que lhe foi dado, como documentar o seu código? O que é javadoc? O que é enumeração em Java? Como utilizar o comando for each? O que são argumentos variáveis? 

Esta seção o ajudará a ter um maior domínio dessas tarefas requisitadas pelo seu chefe.

Agora que você foi apresentado à sua nova situação-problema, estude esta seção e compreenda esses recursos presentes na linguagem Java. Esses recursos são de extrema importância para a construção de qualquer aplicação de pequeno e grande porte. E aí, vamos juntos compreender esses conceitos e, então, resolver esse desafio?

Bom estudo!

CONCEITO-CHAVE

A linguagem Java possui suporte para um recurso muito importante, que é a criação de interfaces. Porém, antes de iniciarmos esse assunto, gostaríamos de explicar um pouco a importância da documentação de código, a utilização do comando for each e como passar uma quantidade variável de parâmetros para um método, bem como apresentar um tipo especial de classe, que é a enumeração.

Documentação de código

Independentemente da linguagem de programação utilizada, construir um código legível e bem documentado é extremamente importante, pois ele necessitará de atualizações e poderá ser lido por outros programadores. A forma mais simples de documentar um código se dá por meio de comentários. 

Existem dois tipos de comentários em Java: 

No entanto, essas duas formas são relativamente pobres e adequadas apenas para pequenos projetos. Dessa maneira, outra forma de se criar a documentação é utilizar o que chamamos de javadoc. Para se criar uma documentação utilizando-se o javadoc, é preciso colocar a documentação dentro da seguinte estrutura /** documentação em javadoc */. De forma a ilustrar como se criar uma documentação em javadoc, analise o Código 3.12 mostrado a seguir. Repare que as linhas 15 a 23 contêm um método totalmente artificial, criado apenas para ilustrar a ideia da documentação.

Código 3.12 | Documentação utilizando tags do javadoc em um método qualquer
/**
 * Colocar a descrição do método aqui. Este é um exemplo de método
 * com dois parâmetros, com retorno e que pode lançar exceções.
 * @param arg1 colocar a descrição do parâmetro arg1. 
 * @param arg2 colocar a descrição do parâmetro arg2. 
 * @return colocar a descrição do valor que é retornado.
 * @throws java.sql.SQLException
 * @throws java.io.IOException
 * @see java.lang.System
 * @see #exemploMetodoLancaMultiExcecoes
 * @since 1.0
 * @version 1.0
 * @author Jesimar da Silva Arantes
 */
public double exemploMetodoCompleto(int arg1, float arg2) 
        throws SQLException, IOException{
    if (arg1 < 0) {
        throw new SQLException()
    } else if (arg2 < 0) {
        throw new IOException();
    }
    return 0.0;
}
Fonte: elaborado pelo autor.

No Código 3.12, podemos perceber que toda a documentação foi colocada dentro da estrutura /** documentação */. Essa estrutura caracteriza uma documentação mais robusta e não apenas um simples comentário. Inicialmente, devemos colocar uma breve descrição do que o método faz, em seguida, podemos acrescentar algumas tags de forma a enriquecer a nossa documentação. Nesse exemplo, foram utilizadas tags como @param, @return, @throws, @see, @since, @version e @author. Analise o Quadro 3.7 para entender para que serve cada uma das tags comentadas. É importante mencionarmos também que esse tipo de tag pode ser utilizado na classe, no método, no construtor, nos atributos e nas interfaces.

Quadro 3.7 | Algumas das tags disponíveis para construção de documentação em Javadoc
Tags
Descrição
@author Nome do autor do código.
@param Descreve o parâmetro recebido pelo método ou construtor.
@return Descreve o que será retornado pelo método.
@throws Descreve a exceção ou o erro lançado pelo método ou construtor.
@see Documenta o código criando um link com outra classe ou método.
@since Documenta a partir de que versão o método foi adicionado.
@version Documenta a versão da classe ou do método.
@deprecated Marca o método como descontinuado ou obsoleto.
Fonte: elaborado pelo autor.

Até agora, essa documentação é parecida com a documentação baseada em comentários, no entanto, contém alguns marcadores. O interessante é que existe uma ferramenta no Java que permite que você gere um código HTML contendo toda a documentação do seu projeto Java. A maioria dos IDEs gera esse HTML apertando-se apenas um botão. Pesquise como fazer isso com a sua IDE de preferência. No Netbeans, basta você ir no menu Executar e, em seguida, ir em Gerar Javador (Nome do Projeto). Após esses passos, na pasta dist será criada uma subpasta chamada javadoc, que conterá diversos arquivos; abra o arquivo index.html em algum navegador de internet e pronto. A partir disso, você poderá acessar toda a documentação do seu código com uma excelente organização estrutural. A Figura 3.5 nos mostra o resultado gerado pela ferramenta javadoc.

Figura 3.5 | Documentação gerada utilizando-se o javadoc do Java
a figura ilustra uma tela com a documentação gerada pelo javadoc.
Fonte: captura de tela do javadoc elaborada pelo autor.

Uma forma de aprender como documentar os seus projetos é observar como são feitas as documentações nas classes nativas do Java. Dessa maneira, entre nas classes Math e System, por exemplo, e observe como elas foram documentadas.

Dica

Caro aluno, pegue alguns dos códigos em Java que você desenvolveu; em seguida, escreva uma documentação utilizando o que você aprendeu sobre javadoc; gere a documentação em formato HTML; analise-a em um navegador de internet; faça diferentes alterações na documentação e analise a saída.

Comando for each

Na Seção 2.2 da Unidade 2, foram vistos alguns laços de repetição, como o for, while e do-while. A linguagem Java suporta outro tipo de comando que controla a repetição, chamado for each (para cada). O comando for each é utilizado, geralmente, para se fazer a iteração em coleções como vetores, matrizes, listas, entre outros. 

Analise o Quadro 3.8 a seguir. 

Quadro 3.8 | Síntese dos comandos de repetição for e for each

Comando: for
Comando: for each
Sintaxe for (Inicializ; ExpLóg; Inc) {

  SeqDeComandos;

}
for (Tipo elem : ColeçãoIteravel) {

  SeqDeComandos;

}
Exemplo int soma = 0;

for (int i = 1; i < 6; i++) {

  soma += i; 
}
int colecao[] = {1, 2, 3, 4, 5};

int soma = 0;

for (int elem : colecao) {

  soma += elem;

}
Fonte: elaborado pelo autor.

O Quadro 3.8 apresenta, inicialmente, a forma geral da sintaxe do comando for each em contraste com o comando for (já estudado); em seguida, vemos um simples exemplo que soma os valores de 1 a 5 utilizando-se o comando for e o comando for each. No exemplo do Quadro 3.8, cada elemento da coleção (vetor) é acessado pela variável elem, que é acumulada na variável soma. 

A seguir, no Código 3.13, podemos ver duas aplicações diferentes utilizando-se o comando for each. Implemente esses métodos e analise a saída em cada um deles para compreendê-los melhor.

Código 3.13 | Exemplos de utilização do comando for each em Java
public static void forEachChar(){
    String livro = "Linguagem Orientada a Objetos";
    for (char letra : livro.toCharArray()){
        System.out.println("character: " + letra);
    }
}
public static void forEachString(){
    String livro[] = {"Linguagem", "Orientada", "a", "Objetos"};
    for (String nome : livro){
        System.out.println("nome: " + nome);
    }
} 
Fonte: elaborado pelo autor.

Agora, vamos imaginar que temos a intenção de criar um método em Java que some dois números e retorne o resultado. No entanto, precisamos criar também um método que some três números e retorne a soma, bem como criar um método que some quatro números e retorne a soma. O Código 3.14 nos apresenta esses três métodos descritos.

Código 3.14 | Métodos em Java que efetuam a soma de 2, 3 e 4 argumentos inteiros
public static int soma(int a, int b){
    return a + b;
}
public static int soma(int a, int b, int c){
    return a + b + c;
}    
public static int soma(int a, int b, int c, int d){
    return a + b + c + d;
} 
Fonte: elaborado pelo autor.

Argumentos variáveis (varargs)

Imagine, agora, que queremos criar um método que some os números inteiros que forem passados como argumentos. Se seguirmos a lógica do código 3.14, não conseguiremos nunca, pois teremos de criar infinitos métodos, acrescentando, em cada um deles, um novo argumento. A linguagem Java, por sua vez, permite-nos fazer isso de forma bastante simples, utilizando a ideia de argumentos variáveis ou varargs. O Quadro 3.9 nos mostra a sintaxe básica em dois exemplos de métodos genéricos que utilizam varargs. No primeiro exemplo, o método recebe apenas um argumento do tipo varargs, que é caracterizado por três pontos (...) depois do tipo de argumento. Já no segundo exemplo, o método recebe um argumento normal seguido de um argumento do tipo varargs.

Quadro 3.9 | Sintaxe de métodos que receberem argumentos variáveis (varargs)
Exemplos de métodos com varargs
modif_acesso tipo_retorno nomeMetodo(TipoArg... args){
    SeqDeComandos;
}
modif_acesso tipo_retorno nomeMetodo(TipoArg arg, TipoArg... args){
    SeqDeComandos;
} 
Fonte: elaborado pelo autor.

De forma a compreender melhor como funcionam os argumentos variáveis, considere o Código 3.15, que nos mostra como podemos criar um método que soma quaisquer quantidades de argumentos passados. 

Código 3.15 | Método capaz de somar todos os valores especificados como argumentos
public class ExemploVarargs {
    public static void main(String[] args) {
        System.out.println(soma());
        System.out.println(soma(6));
        System.out.println(soma(1, 6));
        System.out.println(soma(3, 5, 4));
        System.out.println(soma(2, 7, 4, 8));
        System.out.println(soma(5, 6, 9, 1, 8));
    }
    public static int soma(int... args){
        int valorDaSoma = 0;
        for (int valor : args){
            valorDaSoma += valor;
        }
        return valorDaSoma;
    }
} 
Fonte: elaborado pelo autor.

Nas linhas 10 a 16 do Código 3.15, temos a definição do método soma que recebe uma quantidade variável de argumentos. Nesse código, foi utilizado o comando for each para se fazer a iteração entre cada um dos argumentos recebidos como parâmetros e acumular na variável valorDaSoma. Já o método main, nas linhas 2 a 9, é responsável pelas chamadas do método soma com diferentes quantidades de argumentos. 

Aluno, frente a isso, implemente o código acima e analise a saída da execução. O link para visualização da execução do site Java Tutor está no tópico Referências.

Algumas regras devem ser seguidas ao se utilizar varargs. Apenas o último argumento passado pode ser do tipo varargs. Um parâmetro varargs pode receber zero, muitos ou um array de parâmetros, e os argumentos variáveis podem ser utilizados tanto para métodos quanto para construtores.

Reflita

Reflita sobre o porquê de apenas o último argumento passado para um método ou construtor poder ser do tipo varargs. Quais seriam as dificuldades em se permitir que o primeiro argumento seja do tipo varargs? Reflita sobre por que não se pode ter dois argumentos do tipo varargs em um mesmo método ou construtor. Quais seriam as dificuldades em se permitir dois argumentos varargs?

Enumeração

A linguagem Java dá suporte a um tipo especial de classe chamada enumeração ou, simplesmente, Enum. A sua declaração utiliza a palavra-reservada enum. Uma classe do tipo Enum é utilizada para se fazer a organização de um conjunto de constantes, que, em geral, são static e final. Assim, as constantes são declaradas com letras maiúsculas e são separadas simplesmente por vírgula. Analise o Código 3.16 mostrado a seguir. 

Código 3.16 | Exemplos de declarações de enumerações em Java
public enum DiaDaSemana { 
    DOM, SEG, TER, QUA, QUI, SEX, SAB
}
public enum Mes { 
    JAN, FEV, MAR, ABR, MAI, JUN, JUL, AGO, SET, OUT, NOV, DEZ
}
public enum NivelDeDificuldade { 
    FACIL, MEDIO, DIFICIL 
} 
Fonte: elaborado pelo autor.

No Código 3.16 mostrado acima, a ideia é que cada um dos tipos enum seja codificado em um arquivo .java separado. No entanto, isso não é uma regra, pois se pode ter tipos enum dentro de classes. Vamos analisar a enumeração DiaDaSemana: repare que temos, ao todo, sete constantes, que representam os dias da semana, de domingo a sábado. Em muitos códigos, a definição desse tipo de constante auxilia na legibilidade, ao contrário de números de 0 a 6 para a representação dos dias da semana. Analise, agora, a enumeração NivelDeDificuldade mostrado acima, que define três constantes para o nível fácil, médio e difícil. Nesse caso, também poderíamos ter utilizado alguma outra representação, porém a grande maioria das demais representações perde em legibilidade para a enumeração. A ideia é utilizar enumeração sempre que houver valores que não podem ser trocados, como dias da semana, nomes dos meses, nomes de planetas, cartas do baralho, cores, entre outros.

Exemplificando

Analise o Código 3.17 a seguir que nos mostra como criar um objeto do tipo enumeração. Ao observar esse código, perceba que na linha 3 a criação de uma enumeração é bem simples. Inicialmente, temos o tipo do enum (DiaDaSemana) seguido do nome da variável (dia) e, então, a constante que será atribuída (DiaDaSemana.TER). Da linha 4 a 20, temos um switch-case, que, baseado no valor de entrada, imprime o dia da semana equivalente. 

Diante disso, implemente esse código e faça algumas alterações na variável dia e analise a saída impressa; repare o quanto se ganha em termos de legibilidade de código ao se utilizar a enumeração.

Código 3.17 | Demonstração de uma simples aplicação com enumeração
public class Main {
    public static void main(String[] args) {
        DiaDaSemana dia = DiaDaSemana.TER;
        switch (dia) {
            case DOM:
                System.out.println("Domingo"); break;
            case SEG:
                System.out.println("Segunda"); break;
            case TER:
                System.out.println("Terça"); break;
            case QUA:
                System.out.println("Quarta"); break;
            case QUI:
                System.out.println("Quinta"); break;
            case SEX:
                System.out.println("Sexta"); break;
            case SAB:
                System.out.println("Sábado"); break;
        }
    }
} 
Fonte: elaborado pelo autor.

Interfaces

Nas seções anteriores, estudamos a herança e as classes abstratas. A linguagem Java permite que uma classe herde de apenas uma classe, ou seja, não permite heranças múltiplas, e para sanar essa “limitação”, o Java dá suporte ao conceito de interfaces. Uma interface estabelece um contrato que deve ser seguido ao desenvolver as classes que implementam as interfaces. 

A linguagem Java possui algumas importantes interfaces implementadas. A seguir, apresentaremos uma breve descrição de três delas, que são: Comparable, Runnable e Serializable.

Assimile

O Código 3.18 nos mostra alguns trechos das interfaces Comparable, Runnable e Serializable. É importante destacarmos aqui que a declaração de uma interface utiliza a palavra-reservada interface. De forma geral, as interfaces possuem apenas as assinaturas dos métodos e podem também conter atributos. A classe que implementar a interface é que deverá sobrescrever os métodos. A interface Comparable é genérica, pois recebe o tipo da classe ao utilizar o operador <>. A ideia de classes e interfaces genéricas não será explorada neste livro, assim, esses detalhes de implementação deverão ser abstraídos pelo aluno. A interface Comparable possui apenas um método, que é o compareTo, e a interface Runnable possui apenas um método, chamado run(). Repare que a interface Serializable não possui nada em seu corpo, dessa forma, essa interface foi utilizada como um recurso apenas semântico, já o polimorfismo auxilia nos métodos que serão serializáveis.

Código 3.18 | Trechos das interfaces Comparable, Runnable e Serializable
public interface Comparable<T extends Object> {
    public int compareTo(T o);
}
public interface Runnable {
    public void run();
}
public interface Serializable {
} 
Fonte: elaborado pelo autor.

O Código 3.19 ilustra um exemplo em que temos uma classe chamada Livro que implementa a interface Comparable. Uma classe implementa uma interface utilizando, para isso, a palavra-reservada implements. Aqui, é importante destacarmos que foi colocado entre os símbolos < e > o nome da classe. Esse procedimento poderia ter sido omitido se quiséssemos, mas ele simplifica o processo, evitando-se um casting (conversão de objetos). 

Código 3.19 | Exemplo de classe que implementa a interface Comparable do Java
public class Livro implements Comparable<Livro> {
    public String nome;
    public double custo;
    public Livro(String nome, double custo) {
        this.nome = nome;
        this.custo = custo;
    }
    @Override
    public int compareTo(Livro livro) {
        if (Math.abs(custo - livro.custo) < 0.001) {
            return 0;
        } else if (custo > livro.custo) {
            return 1;
        } else {
            return -1;
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Livro l1 = new Livro("Alice no País das Maravilhas", 38);
        Livro l2 = new Livro("O Senhor dos Anéis", 44);
        Livro l3 = new Livro("Harry Potter", 38);
        int compL1L2 = l1.compareTo(l2);
        int compL1L3 = l1.compareTo(l3);
        int compL2L3 = l2.compareTo(l3);
        System.out.println("Comparação l1 e l2: " + compL1L2);
        System.out.println("Comparação l1 e l3: " + compL1L3);
        System.out.println("Comparação l2 e l3: " + compL2L3);
    }
} 
Fonte: elaborado pelo autor.

O ponto principal a ser destacado no Código 3.19 é a sobrescrita do método compareTo (mostrado nas linhas 8 a 17), que recebe um objeto do tipo Livro como argumento. Esse método compara o preço de dois livros, e se a diferença de preço for menor que um determinado valor limite (0.001), então, o resultado 0 será retornado, indicando que os dois livros têm o mesmo preço. Essa comparação foi feita dessa forma pois a comparação de valores de ponto flutuante tem algumas limitações no computador. Se o preço do livro corrente (atual) for maior do que o preço do livro passado como argumento, então, o resultado 1 será retornado, caso contrário, se o preço do livro corrente for menor do que o preço do livro passado como argumento, o resultado -1 será retornado. Nas linhas 19 a 31, temos a criação de uma classe principal para testar o código. Repare que três objetos do tipo livro foram criados e, em seguida, três testes foram realizados. Caro aluno, reflita sobre qual será a saída do código acima e o que significam esses valores. 

É imprescindível que você compreenda que, ao implementar a interface Comparable e sobrescrever o método compareTo, você poderá fazer comparações diretas entre dois livros. Nesse exemplo, as comparações foram baseadas no preço, mas poderiam ter sido baseadas em outros critérios, como, por exemplo, o nome do livro. Frente a isso, implemente o Código 3.19 e tente adaptá-lo para fazer comparações baseadas no nome, em que o nome que vem antes, de acordo com o alfabeto, deve ser menor do que os nomes que vêm depois. 

Agora, considere este novo exemplo em que temos um carrinho de brinquedo movido à bateria e que é controlado por radiofrequência (RF). Vamos imaginar que, para que esse carrinho se mova, devemos agendar um conjunto de movimentos, e que após cada um dos movimentos executados, 1% da bateria foi gasto. Inicialmente, o carrinho está com sua bateria totalmente carregada (100%). Dito isso, observe a Figura 3.6. 

Figura 3.6 | Exemplo de aplicação que controla um carro rádio controlado
Fonte: elaborada pelo autor.

A Figura 3.6 nos mostra, no quadro (1), como seria esse controle e o carro controlado por RF. Em seguida, no quadro (2), percebemos o estado inicial do carro com 100% da bateria. Posteriormente, no quadro (3), temos um exemplo em que quatro movimentos foram agendados, que foram: cima, direita, cima e esquerda. Após a execução desses quatro movimentos, a bateria caiu para 96%. Lembre-se, a bateria cai 1% a cada movimento executado, conforme as regras descritas acima. Então, no quadro (4), temos mais três movimentos agendados, que foram: baixo, esquerda e esquerda, e após a execução desses três movimentos, o nível de bateria caiu para 93%. Por meio desse exemplo lúdico, podemos explorar uma série de conceitos em Java. Dessa maneira, analise o Código 3.20 que ilustra esse exemplo descrito.

Código 3.20 | Exemplo de classe que implementa a interface Runnable do Java
import java.util.Arrays;
public class CarroRF implements Runnable {
    private int nivelBateria;
    private int numMovimentos;
    public CarroRF() {
        this.nivelBateria = 100;
        this.numMovimentos = 0;
    }
    public void agendarMovimentos(Direcao... direcoesMovimentos) {
        String msg = String.format("Lista movimentos agendados:%s",
                     Arrays.toString(direcoesMovimentos));
        System.out.println(msg);
        numMovimentos += direcoesMovimentos.length;
    }
    @Override
    public void run() {
        System.out.println("Executando movimentos ...");
        //execução da movimentação do carro (abstrair)
        nivelBateria -= numMovimentos;
        numMovimentos = 0;
    }
    @Override
    public String toString() {
        return String.format("Nível da Bateria: %d%n" +
                      "Movimentos a Executar: %d", 
                      nivelBateria, numMovimentos);
    }
}

public enum Direcao {
    CIMA, BAIXO, ESQUERDA, DIREITA;   
}

import static code.unidade3.secao2.ex3.Direcao.*;
public class Main {
    public static void main(String[] args) {
        CarroRF carro = new CarroRF();
        carro.agendarMovimentos(CIMA, DIREITA, CIMA, ESQUERDA);
        System.out.println(carro);
        carro.run();
        System.out.println(carro);
        carro.agendarMovimentos(BAIXO, ESQUERDA, ESQUERDA);
        System.out.println(carro);
        carro.run();
        System.out.println(carro);
    }
} 
Fonte: elaborado pelo autor.

No Código 3.20, a primeira classe apresentada chama-se CarroRF, que modela o carro controlado por radiofrequência e implementa a interface Runnable (executável). Nas linhas 3 a 9, temos dois atributos que modelam o nível da bateria e o número de movimentos agendados. Nas linhas 9 a 14, temos o método agendarMovimentos, que recebe uma varargs de direções possíveis. Nas linhas 15 a 21, temos o método run, que deve ser sobrescrito devido à implementação da interface Runnable. Como esse exemplo é apenas didático, não foi feita nenhuma movimentação do carro, contudo, se fosse implementado o método, este seria feito ali. Dessa maneira, foi dado destaque apenas ao nível corrente da bateria e ao número de movimentos agendados. Nas linhas 30 a 32, um tipo de enumeração foi criado, contendo as quatro direções em que o carro pode ser movimentado. Por fim, nas linhas 34 a 47, temos uma classe para testar a aplicação. Frente a isso, implemente esse código, faça algumas alterações nas direções movidas do carro e analise com calma a saída impressa.

Vamos, agora, ilustrar um exemplo em que criamos a nossa própria interface. Assim, considere que estamos criando uma aplicação para tocar música, que poderá ser executada por um conjunto de instrumentos diferentes. Nesse exemplo, os instrumentos suportados são: piano, violoncelo e violão. A música tocada se baseará em alguma melodia que será passada para cada um dos instrumentos. Analise a Figura 3.7, em que temos quatro classes e uma interface. 

Figura 3.7 | Exemplo de aplicação que toca música a partir de diferentes instrumentos
A figura ilustra a aplicação que toca música. Nela são mostradas a interface instrumento que possui dois métodos tocar e setvolume, as classes Piano, Violoncelo, Violão, a classe meolodia com os métodos musica e tocarNota e a Classe Main com o método main. Das classes piano, violoncelo e violão, saem setas tracejadas com ponta fechada que apontam para a interface instrumento. Da classe Main saem setas com ponta aberta que apontam para as classes piano, violoncelo, violão e melodia.
Fonte: elaborada pelo autor.

Analise, agora, o Código 3.21, que foi construído a partir da descrição acima e da Figura 3.7.

Código 3.21 | Exemplo que toca música e criação da interface Instrumento
public interface Instrumento {
    public void tocar(Melodia melodia);
    public void setVolume(int volume);
}

import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;
public class Piano implements Instrumento {
    private int volume;
    private final Synthesizer sintetizador;
    public Piano() throws MidiUnavailableException {
        sintetizador = MidiSystem.getSynthesizer();
    }
    @Override
    public void tocar(Melodia melodia) {
        try {
            sintetizador.open();
            MidiChannel canal = sintetizador.getChannels()[0];
            canal.programChange(1);//código ID do piano
            melodia.musica(canal, volume);
            sintetizador.close();
        } catch (MidiUnavailableException | 
                 InterruptedException ex) {
            System.out.println(ex);
            sintetizador.close();
        }
    }
    @Override
    public void setVolume(int volume) {
        this.volume = volume;
    }
}

public class Violoncelo implements Instrumento {
    //o código foi omitido, pois é similar a classe Piano
    //acesse https://github.com/jesimar/Livro-POO-Java
}

public class Violao implements Instrumento {
    //o código foi omitido, pois é similar a classe Piano
    //acesse https://github.com/jesimar/Livro-POO-Java
}

import javax.sound.midi.MidiChannel;
public class Melodia {
    private final static int DO = 60;
    private final static int RE = 62;
    private final static int MI = 64;
    private final static int FA = 65;
    private final static int SOL = 67;
    private final static int SILENCIO = 0;
    private final int UM_TEMPO;
    private final int DOIS_TEMPOS;
    public Melodia(double velocidade) {
        UM_TEMPO = (int)(250/velocidade);
        DOIS_TEMPOS = (int)(500/velocidade);
    }
    public void musica(MidiChannel canal, int volume) 
                throws InterruptedException{
        tocarNota(canal, DO, UM_TEMPO, volume);
        tocarNota(canal, RE, UM_TEMPO, volume);
        tocarNota(canal, MI, UM_TEMPO, volume);
        tocarNota(canal, FA, DOIS_TEMPOS, volume);
        tocarNota(canal, FA, UM_TEMPO, volume);
        tocarNota(canal, FA, UM_TEMPO, volume);
        tocarNota(canal, SILENCIO, UM_TEMPO, volume);
    }
    private static void tocarNota(MidiChannel canal, int nota, 
            int tempo, int volume) throws InterruptedException {
        canal.noteOn(nota, volume);
        Thread.sleep(tempo);
        canal.noteOff(nota, volume);
    }            
}

import javax.sound.midi.MidiUnavailableException;
public class Main {
    public static void main(String[] args) 
                throws MidiUnavailableException {
        System.out.println("Tocando melodia piano vel. normal");
        Melodia melodiaNormal = new Melodia(1.00);
        Piano piano = new Piano();
        piano.setVolume(75);
        piano.tocar(melodiaNormal);
        System.out.println("Tocando melodia celo vel. rápida");
        Melodia melodiaRapida = new Melodia(1.25);
        Violoncelo violoncelo = new Violoncelo();
        violoncelo.setVolume(100);
        violoncelo.tocar(melodiaRapida);
        System.out.println("Tocando melodia violão vel. lenta");
        Melodia melodiaLenta = new Melodia(0.75);
        Violao violao = new Violao();
        violao.setVolume(50);
        violao.tocar(melodiaLenta);
    }
} 
Fonte: elaborado pelo autor.

No Código 3.21, nas linhas 1 a 4, temos a interface Instrumento, que possui a assinatura de dois métodos, que são: tocar e setVolume. Nas linhas 6 a 34, temos a classe Piano, que implementa a interface Instrumento criada. Você deve ter reparado que um conjunto de classes do pacote javax.sound.midi foi utilizado. Não será explicado em detalhes como funcionam as classes desse pacote, mas, por hora, você apenas precisa saber que elas manipulam dados para tocar notas musicais em um determinado canal de saída. Assim, para que esse código funcione, o computador que executá-lo deverá ter caixas de som para saída do áudio. Nas linhas 36 a 44, temos as classes Violoncelo e Violao, que foram omitidas por serem semelhantes à classe Piano. Nas linhas 46 a 76, temos a classe Melodia, que define a música a ser tocada fazendo uso das notas musicais. Por fim, nas linhas 78 a 98, temos a classe responsável por organizar a execução da aplicação. Diante disso, analise com calma as linhas desse código e tente compreender a sua lógica.

Ao desenvolvermos um código, às vezes, ficamos confusos sobre qual recurso utilizarmos: classe abstrata ou interface, mas, quanto a isso, fique tranquilo, isso é natural, às vezes, podemos utilizar um ou outro, e ambos funcionarão, desde que utilizados da forma correta. O Quadro 3.10 sintetiza de forma bastante objetiva algumas das características entre as classes abstratas e as interfaces em Java. Analise com cautela esse quadro, mas não se preocupe em memorizar todas as informações, em geral, isso não é necessário.

Quadro 3.10 | Síntese de algumas características de classes abstratas e interfaces
Características
Classe Abstrata
Interface 
Pode possuir construtores Sim Não
Pode possuir métodos concretos Sim Não
Pode possuir métodos abstratos Sim Sim
Pode possuir métodos final Sim Não
Pode possuir métodos não-final Sim Sim
Pode possuir métodos estáticos Sim Sim
Pode possuir atributos final Sim Sim
Pode possuir atributos não-final Sim Não
Pode possuir atributos estáticos Sim Sim
Pode possuir atributos não-estáticos Sim Não
Fonte: elaborado pelo autor.

O Quadro 3.11 a seguir apresenta uma síntese da comparação entre as classes abstratas e as interfaces. Caro aluno, analise este quadro.

Quadro 3.11 | Comparação entre as classes abstratas e interfaces em Java
Características
Classe Abstrata
Interface
Palavra-reservada utilizada. Abstract interface
Exemplo de declaração. public abstract class X public interface X
Exemplo de utilização. public class Y extends X public classimplements X
Relação de herança. Uma classe abstrata pode herdar apenas uma classe (abstrata ou não). Uma interface pode herdar de múltiplas interfaces.
Relação de implementação. Uma classe pode herdar apenas uma classe abstrata. Uma classe pode implementar múltiplas interfaces.
Modificador de acesso dos métodos e atributos. Pode ser público, default, protegido ou privado. É público por padrão.
Modificador dos atributos. Pode ser final, não final, estático ou não estático. É final por padrão. É estático por padrão.
Instanciação. Não permitida. Não permitida.
Fonte: elaborado pelo autor.

A linguagem Java ganha em aspectos de reusabilidade de código ao dar suporte à herança, classe abstrata e interface, e o polimorfismo também pode ser utilizado com interfaces. Dessa maneira, pode-se criar um objeto do tipo da interface com a instanciação do objeto do tipo da classe que implementa a interface.

Saiba mais

Até agora, nada foi dito a respeito da identação de código. É importante que os códigos desenvolvidos sejam indentados corretamente, uma vez que colaboram muito na identificação de erros. 

Dica: muitos Ambientes de Desenvolvimento Integrados (IDEs) têm recursos de autoidentação. No Netbeans, basta ir no menu Código-Fonte e, em seguida na opção Formatar (ou, então, no atalho Alt+Shift+F). No Eclipse, o atalho é Ctrl+Shift+F; já no IntelliJ IDEA, o atalho é Ctrl+Alt+I.

Pesquise mais

O texto apresentou brevemente o conteúdo de enumeração em Java. Este assunto, apesar de simples, é muito importante. Dessa maneira, assista ao vídeo sugerido abaixo para entender melhor como aplicar na prática esse conteúdo.

LOIANE GRONER. Curso de Java 53: enumeradores (Enum). 2016. 

LOIANE GRONER. Curso de Java 54: enumeradores como classe (construtor e métodos). 2016. 

LOIANE GRONER. Curso de Java 55: Enum: métodos value e valueOf. 2016. 

Caro estudante, nesta seção você estudou os conteúdos relacionados à documentação com javadoc, ao comando for each, ao método com argumentos variáveis, à enumeração, a interfaces e à comparação entre classes abstratas e interfaces, bem como foram mostrados diversos exemplos desses conceitos em Java. Aprenderemos, na próxima seção, como construir aplicações que utilizam interfaces gráficas, avançando ainda mais no entendimento da linguagem Java.

Referências

CURSO EM VÍDEO. Curso de Java para iniciantes – grátis, completo e com certificado. 2019. Disponível em: https://bit.ly/35ykFEQ. Acesso em: 20 ago. 2020.

ORACLE. Interface Comparable<T>. [s.d.]. Disponível em: https://bit.ly/35Acisd. Acesso em: 20 ago. 2020.

ORACLE. Interface Runnable. [s.d.]. Disponível em: https://bit.ly/3mjUaJh. Acesso em: 20 ago. 2020.

ORACLE. Interface Serializable. [s.d.]. Disponível em: https://bit.ly/2ZArCl5. Acesso em: 20 ago. 2020.

PYTHON TUTOR. Java Tutor - visualize Java code execution to learn Java online. [s.d.]. Disponível em: https://bit.ly/2FryN84. Acesso em: 24 jul. 2020.

Bons estudos!

AVALIE ESTE MATERIAL

OBRIGADO PELO SEU FEEDBACK!