Java: O que é Interface ?

[Dica: Leia os comentários do código e implemente-os para melhor entendimento.]

Como você já aprendeu na Orientação a Objetos, os objetos representam algo do mundo real através de seus atributos e métodos. Uma interface no java pode representar uma superfície como o botão de liga e desliga de um equipamento eletrônico. Este botão, por padrão, esta obrigatóriamente presente em todos os aparelhos de televisão.

No mais comum, uma interface, é um conjunto de métodos relacionados que não tem corpo, ou seja, sem implementação. Se um comportamento básico de qualquer aparelho eletrônico for especificado como uma interface, poderia ser como o código abaixo:

package estudos;

public interface Eletronico {
    void ligar();
    void desligar();
}

Todos os métodos das interfaces são implicitamente public e abstract , sendo que muitas pessoas gostam usualmente de explicitar o public. E as constantes são public static e final.

package estudos;

String teste = "t123";

public interface Eletronico {
    void ligar();
    void desligar();
}

É a mesma coisa que:

package estudos;

public static final String teste = "t123";

public interface Eletronico {
    public abstract void ligar();
    public void desligar();
}

Se você quiser implementar um aparelho eletrônico seguindo o padrão básico de comportamento, você terá que usar a palavra-chave implements na declaração da classe deste aparelho:

package estudos;

public class Televisao implements Eletronico {

    // Temos obrigatóriamete que implementar os métodos
    // da interface Eletronico.

    public void ligar() {
        System.out.println("Liguei a televisão");
        // Aqui você implementaria os códigos para ligar a televisão
    }

    public void desligar() {
        System.out.println("Desliguei a televisão");
        // Aqui você implementaria os códigos para desligar a televisão
    }

}

Como vemos no código, para se implementar a televisão, que é um aparelho eletrônico, seguimos o padrão criado por nós para aparelhos eletrônicos. Usamos a palavra-chave implements para implementar obrigatóriamente os métodos da interface Eletronico , fazendo uso do padrão criado. É como um contrato entre diversas classes, quase que criando até um tipo de dado.

Quando você precisa criar funções que manipulam outros objetos, é necessário, as vezes, garantir que esta função esta realmente recebendo um objeto que tenha determinados atributos e métodos, os quais esta irá fazer uso. Vou mostrar um exemplo:

Criamos um interface Imprimivel:

package estudos2;

public interface Imprimivel {

    void imprimir();

}

Vamos criar uma classe Impressora que implementa a interface Eletronico, e que só imprime objetos que implementam a interface Imprimivel.

package estudos;

public class Impressora implements Eletronico {

    // Impressora desligada por padrão
    private boolean ligada = false;

    // Método para imprimir uma nota, veja o parâmetro
    // Imprimivel objImprimivel
    // O metodo espera um objeto que possua uma função imprimir()
    // gerou um contrato da impressora .. o método somente
    // aceita objetos que implementa a interface Imprimivel.
    public void imprimirNotaFiscal(Imprimivel objImprimivel){
        // Verifica se a impressora esta ligada!
        if(this.ligada){
            objImprimivel.imprimir();
            // Se o objeto passado não fosse Imprimivel
        } else {
            System.out.println("Problema: Impressora desligada!");
        }
    }

    // implementamos o método da interface Eletronico
    // Ligamos a impressora ....
    public void ligar() {
        this.ligada = true;
        System.out.println("Impressora ligada!");
    }

    public void desligar() {
        this.ligada = false;
        System.out.println("Impressora desligada!");
    }

}

Agora vamos criar dois tipos de nota fiscal, uma é imprimivel na impressora e a outra não aceita imprimir pela impressora do seu sistema, porque a segunda é somente para visualização.

Classe NotaFiscal que implementa a interface Imprimivel:

package estudos;

public class NotaFiscal implements Imprimivel {

    private int numero;
    private double valor;

    public NotaFiscal(int numero, double valor){
        this.numero = numero;
        this.valor  = valor;
    }

    public void imprimir() {
        System.out.println("Número: " + this.getNumero() + ", Valor: "+ this.getValor());
    }

    public double getValor() {
        return valor;
    }

    public void setValor(double valor) {
        this.valor = valor;
    }

    public int getNumero() {
        return numero;
    }

    public void setNumero(int numero) {
        this.numero = numero;
    }

}

>

A Classe de nota fiscal que é somente para visualização. Não é obrigada a implementar a função imprimir().

package estudos;

public class NotaFiscalOnline {
    private int numero;
    private double valor;

    public NotaFiscalOnline(int numero, double valor){
        this.numero = numero;
        this.valor  = valor;
    }

    public double getValor() {
        return valor;
    }

    public void setValor(double valor) {
        this.valor = valor;
    }

    public int getNumero() {
        return numero;
    }

    public void setNumero(int numero) {
        this.numero = numero;
    }
}

Criamos um classe executável “main” para fazer uma demonstração:

package estudos;

public class Main {

    public static void main(String[] args) {

        // Instânciamos uma impressora
        Impressora impressora1510 = new Impressora();

        // Ligamos a impressora!
        impressora1510.ligar();

        // Fazemos um for para simular a criação de 10 notas fiscais
        for (int i = 1; i <= 10; i++) {
            // numero da nota atual recebe o valor de i
            int numeroNota = i;
            // Criamos um valor i * 10, só para esta demonstração
            // 10.0 , 20.0, 30.0, 40.0, 50.0 .... 100.0
            double valor = (double) (10 * i);
            // Criamos um nota fiscal aqui dentro do for...
            // utilizando o valores gerados com a ajuda da variável i
            NotaFiscal notaf = new NotaFiscal(numeroNota, valor);
            // Como passamos um objeto que implementa a classe Imprimivel para
            // o método da impressora que espera um objeto Imprimivel
            // não terá problemas...
            impressora1510.imprimirNotaFiscal(notaf);
        }
    }

}

Primeiro criamos a interface Imprimivel, depois uma Classe Impressora que implementa a interface Eletronico. Criamos duas classes de nota fiscal, uma é Imprimivel e a outra não. Então veio o teste na classe executável:

O resultado foi:

Impressora ligada!
Número: 1, Valor: 10.0
Número: 2, Valor: 20.0
Número: 3, Valor: 30.0
Número: 4, Valor: 40.0
Número: 5, Valor: 50.0
Número: 6, Valor: 60.0
Número: 7, Valor: 70.0
Número: 8, Valor: 80.0
Número: 9, Valor: 90.0
Número: 10, Valor: 100.0

Vou comentar um pouco sobre a linha 24 do código da Classe executável “Main”:

impressora1510.imprimirNotaFiscal(notaf);

Se tivessemos passado um objeto que não fosse do “tipo” Imprimivel, aconteceria um erro na hora da compilação! Não chegava nem ao ponto de compilar. Isso é ótimo para obrigar ao programador a passar pelo parâmetro um objeto que tenha realmente a função imprimir(), a qual a Classe Impressora necessita.

Tente alterar , passando um objeto que não implemente a interface Imprimivel, para ver o que acontece!

As regras de nomenclatura para interfaces são as mesmas que para as classes, embora seja usual o emprego de nomes que denotem, ou indiquem, suas funcionalidades ali definidas. Ou seja, uma interface chamada Imprimivel, somente terá métodos relacionados com impressão, e uma interface Editavel terá métodos relacionados a edição.

As interfaces também fazem uso do mecanismo de herança! Sim, isto mesmo … extends. Suas funcionálidades podem ser reescritas, ou podem ganhar mais funcionalides através da herança.

package estudos;
public interface Editavel {

    void editar(String conteudo);

}
package estudos;
public interface EmailEditavel extends Editavel {

    void editarAssunto(String assunto);
    void editarDe(String de);
    void editarPara(String para);

}

A interface ficou mais especialista , ganhou funções relacionadas a edição de emails, ficou com 4 funções, uma já era de edição do conteúdo, editar(String conteudo), herdado da interface “Pai” (Editavel).

Uma classe pode implementar diversas interfaces, por exemplo:

Um e-mail pode ser Imprimivel e Editavel…

package estudos2;

public class Email implements Imprimivel, EmailEditavel {

    private String de;
    private String para;
    private String assunto;
    private String mensagem;

    // Implementamos o método imprimir() da interface Imprimivel
    public void imprimir() {
        System.out.println(
                "De:" + this.getDe() + ",\n" +
                "Para:" + this.getPara() + ",\n" +
                "Assunto: " + this.getAssunto() + ",\n" +
                "Mensagem:" + this.getMensagem());
    }

    // E implementamos os métodos da interface EmailEditavel!
    public void editarAssunto(String assunto) {
        this.setAssunto(assunto);
    }

    public void editarDe(String de) {
        this.setDe(de);
    }

    public void editarPara(String para) {
        this.setPara(para);
    }

    public void editar(String conteudo) {
        this.setMensagem(conteudo);
    }

    public String getDe() {
        return de;
    }

    public void setDe(String de) {
        this.de = de;
    }

    public String getPara() {
        return para;
    }

    public void setPara(String para) {
        this.para = para;
    }

    public String getAssunto() {
        return assunto;
    }

    public void setAssunto(String assunto) {
        this.assunto = assunto;
    }

    public String getMensagem() {
        return mensagem;
    }

    public void setMensagem(String mensagem) {
        this.mensagem = mensagem;
    }

}

Com isto, podemos ver que diferente do C++, o java não tem herança multipla “extends Classe1, Classe2″, mas uma classe pode implementar mais de uma interface. O Java não permite a herança multipla, porque se tivemos duas classes, que cada uma tem exatamente o mesmo método(mesma assinatura) e com corpo diferente, qual dos dois métodos a JVM irá usar?

Exemplo:

Se pudessemos fazer herança multipla no java ficaria assim:

Classe PortuguesBrasil:

package estudos;

public class PortuguesBrasil {
    public void falarPalavraFiladeBanco(){
        System.out.println("Fila!");
    }
}

Classe PortuguesPortugal:

package estudos;

public class PortuguesPortugal {
    public void falarPalavraFiladeBanco(){
        System.out.println("Bicha!");
    }
}

Classe Pessoa:

package estudos;

public class Pessoa extends PortuguesBrasil, PortuguesPortugal {
    public Pessoa(){
        this.falarPalavraFiladeBanco();
    }
}

Isto é impossível no java! Este código não pode ser compilado! Qual dos dois métodos a JVM irá usar? falarPalavraFiladeBanco() da classe PortuguesBrasil ou PortuguesPortugal ? A interface veio para resolver esse tipo problema.

Algumas últimas pergutas:

É possível declarar Atributos privados em uma Interface?
Não. As interfaces não podem ter atributos privados, todos os seus atributos são public final e static. O objetivo das interfaces é que sejam implementadas, as classes que a implementa irá fazer uso de seus atributos, se tivesse atributos private seria inútil, pois a classe que implementará não enxegará o atributo. Por este motivo que não é permitido criar atributos ou métodos private em interfaces.

É possível declarar métodos static em uma interface.
Não. Uma interface não pode ter métodos com corpo. Para que um método static ? Se os métodos de interfaces não podem ter corpo. Os métodos das interfaces, serão implementados em outras classes.

Termino aqui! Mais tarde quando for falar de Design Patterns comento mais sobre interfaces.

Comentem!

Valeu!