Interfaces

As classes incorporam duas funções interligadas:

  • elas são um mecanismo de reúso de código;
  • elas estabelecem a "interface" dos objetos, ou seja, que atributos e métodos estão disponíveis para serem usados por um cliente externo.

Já vimos anteriormente alguns problemas de usar somente a herança como mecanismo de reúso. O mesmo acontece com a interface. Muitas vezes queremos que objetos compartilhem a mesma interface, sem que usem a herança para isso.

Por essa razão o Java define o mecanismo chamado interface. Ele permite a declaração de um interface que será garantida por um conjunto de classes, sem a necessidade que elas estejam ligadas por herança.

Considere as duas classes a seguir que representam formas geométricas. Ambas oferecem métodos para cálculo do perímetro e área, entretanto, não compartilham código.

Suponha que se deseja padronizar o acesso às interfaces de ambos objetos resolvendo a chamada de métodos de modo polimórfico. É possível se fazer isso sem herança, como será apresentado no próximo bloco.


In [1]:
public class Retangulo {
    private int altura;
    private int largura;

    public Retangulo(int altura, int largura) {
        this.altura = altura;
        this.largura = largura;
    }
    
    public int getAltura() {
        return altura;
    }
    
    public int getLargura() {
        return largura;
    }
    
    public float getPerimetro() {
        return 2 * (altura + largura);
    }
    
    public float getArea() {
        return altura * largura;
    }
}

public class Circulo {
    public static float PI = 3.1416f;
    
    private int raio;
    
    public Circulo(int raio) {
        this.raio = raio;
    }
    
    public int getRaio() {
        return raio;
    }
    
    public float getPerimetro() {
        return 2 * Circulo.PI * raio;
    }
    
    public float getArea() {
        return Circulo.PI * raio * raio;
    }
}

Retangulo rt = new Retangulo(6, 10);
System.out.println("Perímetro do retângulo: " + rt.getPerimetro());
System.out.println("Área do retângulo: " + rt.getArea());

Circulo cc = new Circulo(8);
System.out.println("Perímetro do círculo: " + cc.getPerimetro());
System.out.println("Área do círculo: " + cc.getArea());


Perímetro do retângulo: 32.0
Área do retângulo: 60.0
Perímetro do círculo: 50.2656
Área do círculo: 201.0624

Declarando e Implementando uma interface

Uma interface em Java declara um conjunto de métodos que deverão ser implementados por todas as classes que implementa a interface. A seguinte declaração da interface Geometria:

public interface Geometria {
    public float getPerimetro();
    public float getArea();
}

Indica que todos as classes que a implementarem precisarão implementar getPerimetro() e getArea() com as assinaturas indicadas.

Qualquer classe pode indicar que implementará a interface Geometria com a cláusula implements. Uma vantagem das interfaces sobre a herança (quando a intenção é padronizar a interface) é que uma classe pode implementar várias interfaces.

A seguinte declaração:

Geometria g;

Define uma variável g que é capaz de manter uma referência para qualquer objeto de classe que implementa a interface Geometria. Por essa razão são permitidas as instanciações:

Geometria g = new Retangulo(6, 10);
g = new Circulo(8);

Pode-se chamar qualquer método declarado da interface Geometria e a execução é polimórfica, ou seja, depende da instância.


In [1]:
public interface Geometria {
    public float getPerimetro();
    public float getArea();
}

public class Retangulo implements Geometria {
    private int altura;
    private int largura;

    public Retangulo(int altura, int largura) {
        this.altura = altura;
        this.largura = largura;
    }
    
    public int getAltura() {
        return altura;
    }
    
    public int getLargura() {
        return largura;
    }
    
    public float getPerimetro() {
        return 2 * (altura + largura);
    }
    
    public float getArea() {
        return altura * largura;
    }
}

public class Circulo implements Geometria {
    public static float PI = 3.1416f;
    
    private int raio;
    
    public Circulo(int raio) {
        this.raio = raio;
    }
    
    public int getRaio() {
        return raio;
    }
    
    public float getPerimetro() {
        return 2 * Circulo.PI * raio;
    }
    
    public float getArea() {
        return Circulo.PI * raio * raio;
    }
}

Geometria g = new Retangulo(6, 10);

System.out.println("Perímetro do retângulo: " + g.getPerimetro());
System.out.println("Área do retângulo: " + g.getArea());

g = new Circulo(8);

System.out.println("Perímetro do círculo: " + g.getPerimetro());
System.out.println("Área do círculo: " + g.getArea());


Perímetro do retângulo: 32.0
Área do retângulo: 60.0
Perímetro do círculo: 50.2656
Área do círculo: 201.0624

Classes Abstratas x Interfaces

As classes abstratas e interfaces têm uma sobreposição de funções. Alguns são levados a acreditar que uma interface é uma classe com todos os métodos abstratos.

Exercício

Para entender as diferenças, retome a classe abstrata ListStr cuja herdeira você implementou mo notebook de classes abstratas. Se você transformar ListStr na interface IListStr com o intuito de não usar mais classes abstratas, escreva abaixo como ficaria o código da interface e o código modificado das classes. Escreva uma sequência de instruções que usem a interface e as respectivas classes.


In [2]:
public interface ListStr {
   public abstract String first();
   public abstract String next();
   public void list();
}

public class ListImpl implements ListStr {
    private String list[];
    private int current = 0;
    
    public ListImpl(String list[]) {
        this.list = list;
    }
    
    public String first() {
        current = 0;
        return currentElement();
    }
    
    public String next() {
        current++;
        return currentElement();
    }
        
    public String currentElement() {
        String result = null;
        if (list != null &&
            current >= 0 && current < list.length)
            result = list[current];
        return result;
    }
    
    public void list() {
      String element = first();
      
      while (element != null) {
         System.out.println(element);
         element = next();
      }
   }
}

String l[] = {"Asdrubal", "Doriana", "Gumercindo"};
ListStr aList = new ListImpl(l);
aList.list();


Asdrubal
Doriana
Gumercindo

Discussão

Rotinas genéricas que usam métodos abstratos

Por um lado, você notará que não é possível se implementar nenhum método na interface, o que impede de criar abordagem de métodos que usam outros métodos potenciais abstratos, como é o caso do list(). Isso é uma vantagem das classes abstratas que você terá que simular de outro modo.

Múltiplas interfaces

Por outro lado, uma classe pode ter inúmeras interfaces mas só pode ser herdeira de uma classe abstrata, o que limita o poder dessas classes de agir como padronizador de interfaces.