Utilizando
Interfaces
As interfaces são
um dos elementos que proporciona maior versatilidade ao Java.
Porém,
é um dos conceitos mais abstratos para quem está
começando
a aprender a linguagem, e muitas vezes não é
utilizado
todo seu potencial mesmo por programadores mais experientes.
Antes de começar a discutir onde usá-las, vamos entender
o
que são. Uma interface pode ser vista como um protocolo de
comportamento.
Ela é simplesmente uma lista de métodos abstratos,
podendo
também incluir variáveis. Para utilizá-la,
é
criada uma classe que a implemente. Esta classe será obrigada a
definir
todos os métodos das interfaces que está implementando,
como
mostrado no exemplo abaixo, em que temos as interfaces Leitor e
Programador sendo
implementadas pela classe ParticipanteForum.
interface Leitor {
String lendo();
}
interface
Programador {
void pensando(char[] ideias);
String digitando();
}
class
ParticipanteForum implements Leitor, Programador {
String pensamento;
public String lendo() { // método definido na interface Leitor
return
"Forum";
}
public void pensando(char[]
ideias) { // método definido na interface Programador
pensamento =
new String(ideias);
}
public String digitando()
{ // método definido na interface Programador
return
pensamento;
}
private String aprendendo()
{ // método exclusivo desta classe
return "Java";
}
}
public
class Demonstracao {
public static void main(String[]
args) {
ParticipanteForum participante
= new ParticipanteForum (); // instanciado o objeto
Leitor leitor = participante; // upcast para Leitor
System.out.println("O
participante está lendo " + leitor.lendo());
Programador
programador
= participante; // upcast para Programador
String java = "Java";
programador.pensando(java.toCharArray());
System.out.println ("E
programando " + programador.digitando());
}
}
Neste exemplo, criamos um objeto "participante" da classe ParticipanteForum,
e em seguida referenciamos este objeto por uma variável do tipo Leitor
e depois do tipo Programador.
Uma analogia muito utilizada é dizer que a classe assina um
contrato
com a interface, se comprometendo a implementar seus métodos, e
em
troca os objetos desta classe poderão ser vistos como sendo do
tipo
definido pela interface (através de polimorfismo). De certa
forma,
interfaces são parecidas com classes abstratas, mas há
algumas
diferenças fundamentais:
- Uma classe pode implementar várias interfaces (herança
múltipla),
mas apenas uma super classe;
- Uma interface não pode implementar nenhum método,
enquanto
uma classe abstrata em geral possui alguns métodos concretos e
outros
abstratos.
Interfaces na API do Java
Dentro da API básica do J2SE encontramos vários exemplos
interessantes
do uso de interfaces.
O primeiro que as pessoas que estão iniciando no Java costumam
encontrar
é a interface Cloneable.
Sua função é
indicar que o método clone() pode ser
utilizado para aquela
classe. Na verdade, ela é necessária apenas porque o
método
clone() da classe Object realiza uma
checagem em tempo
de execução. Outro exemplo de interface de
marcação
é a Serializable,
indicando se o objeto poderá ser
serializado ou não.
Outro uso de interfaces é demonstrado no collections
framework.
Neste caso, temos algumas interfaces principais, como a List, Set
e Map, e um maior
número de classes implementando-as. Por
exemplo, no caso da interface List, há quatro
classes que
podem ser utilizadas: AbstractList,
ArrayList, LinkedList
e Vector. Estas
classes são agrupadas sob a mesma interface
pois têm como característica comum serem uma lista
ordenada
de elementos. A interface define o comportamento básico que
estas
classes deverão implementar, tais como possuir o método add(Object
o) para adicionar um elemento e o get(int index) para
retornar
o elemento de uma determinada posição. A diferença
está
em como irão realizar estas tarefas, pois cada uma está
otimizada
de uma forma. Por exemplo, se o programa freqüentemente
provê acesso
aleatório a dados da lista, a ArrayList oferece um
acesso
rápido aos elementos individuais. Este acesso rápido se
dá
através de um custo de lentidão nas
operações
de adicionar e remover elementos no meio da lista. Se este segundo
comportamento
será o mais utilizado, então a classe LinkedList oferece
uma melhor alternativa, provendo um rápido acesso
seqüencial, adições
e exclusões, ao custo de um acesso aleatório mais lento.
Mas
você só precisará escolher entre uma classe ou
outra
na hora de instanciar o objeto, pois pode referenciá-lo
através
de uma variável do tipo List para
utilizá-lo depois,
como no exemplo abaixo:
List minhaLista = new LinkedList();
/* O método add é parte da interface
List
e está implementado na classe LinkedList */
minhaLista.add("elemento 1");
Aqui, criamos a variável minhaLista a partir da
classe LinkedList,
porém, se no decorrer do desenvolvimento percebermos que uma ArrayList
teria uma melhor performance, basta trocar a classe na linha de
código
em que foi instanciado o objeto. Em todo resto a variável minhaLista
é vista apenas como sendo do tipo List e utiliza apenas os
métodos declarados nesta interface, o que poderá
permanecer
igual dado que ambas as classes implementam a interface List. Assim,
como o próprio tutorial na Sun diz, entendendo como usar as
interfaces,
você sabe a maior parte do framework.
Mais um exemplo interessante é o da interface ResultSet.
Ela tem a função de representar uma tabela com os dados
retornados
da execução de uma query em um banco de dados.
Métodos
para manipular seus dados são declarados nesta interface, tais
como
next() para ir para
o próximo registro e getString(String
columnName) para retornar o valor de um campo. Porém,
nenhuma
classe incluída no J2SE implementa esta interface. O que
acontece
neste caso é os fabricantes dos bancos de dados implementarem o ResultSet
em seus drivers. Desta forma, o desenvolvedor precisa apenas trabalhar
com
um objeto do tipo ResultSet,
não importando o funcionamento
interno da classe de cada fabricante. Novamente, as interfaces
facilitando
nossa vida, diminuindo a necessidade do entendimento dos detalhes das
implementações
individuais e permitindo uma troca de fabricante com maior facilidade.
A
interface de ResultSet
inclui, além dos métodos, diversos
campos (constantes), que são utilizados para passar
parâmetros
para o método que retornará o ResultSet.
Estes foram apenas alguns exemplos para demonstrar os benefícios
que
uma interface pode trazer. Existem vários outros que podem ser
estudados
no J2SE.
Mas agora, vamos ver algumas considerações importantes
para
utilizar suas próprias interfaces.
Criando interfaces
A declaração da interface é semelhante à de
classes
abstratas. Elas podem ser declaradas públicas, ou então
ficam
com acesso padrão, ou seja, visível dentro do mesmo
pacote.
A herança para as interfaces funciona de forma um pouco
diferente.
Uma interface extends uma ou mais interfaces. Se você
tentar
estender uma classe ou implementar uma interface em outra interface,
seu
programa não irá compilar.
Todos os métodos de uma interface são implicitamente
públicos
e abstratos, tanto faz você colocar estes modificadores
explicitamente
ou não, e o compilador acusará um erro se você
tentar
declará-los de qualquer outra forma. Considerando que a
implementação
do método cabe às classes e não à
interface,
você também não pode utilizar modificadores como static,
final, native, strictfp ou synchronized.
Variáveis declaradas em interfaces sempre serão public
static final, ou seja, constantes.
Ao planejar uma interface é importante lembrar-se de colocar
todos
os métodos que serão necessários logo no
início.
Aumentar a definição de métodos depois é
sempre
perigoso, pois poderá causar um impacto em classes que já
estavam
implementando a interface e agora terão que adicionar
também
os novos métodos para conseguirem ser compiladas.
Implementando interfaces
É necessário implementar os métodos das interfaces
apenas
na primeira classe concreta da hierarquia. Desta forma, classes
abstratas
têm a opção de implementá-los ou não.
Todos
os métodos herdados de interfaces devem ser declarados public,
seguindo as regras tradicionais de herança (não é
permitido
diminuir o acesso em uma classe filha). De forma similar, os
métodos
da classe filha não podem declarar exceções que
estejam
fora do escopo da exceção do método da interface.
Por
exemplo, se o método da interface lançar uma Exception,
o método implementado poderá estar lançando
qualquer
exceção. Já se na interface a
declaração
for mais específica, lançando uma SQLException, o
compilador acusará um erro ao tentar implementar o método
lançando
uma IOException ou
uma Exception
genérica. No caminho
inverso não há problema: os métodos das classes
podem
deixar de lançar alguma exceção que esteja
declarada
na interface.
Quando usar
A aplicação mais óbvia das interfaces é
para
simular herança múltipla, mas não é a
única,
como visto nos exemplos do próprio J2SE.
No primeiro exemplo (Cloneable)
vimos interfaces de marcação.
Na prática, você pode querer criar um método e
restringir
seu uso a classes que aleguem ter uma certa
conscientização
sobre seu funcionamento, "assinando o contrato". Então
você
obriga que esta classe implemente a interface utilizando um
código
semelhante a:
public void metodoRestrito(Object obj) throws
Exception
if (obj instanceof InterfaceMarcacao) {
// Código realizado pelo
método
} else {
throw new Exception ("Favor assinar o
termo
de responsabilidade");
}
Os outros exemplos mostram as vantagens mais importantes de interfaces.
Seu
uso desde o início de um projeto pode poupar muito tempo no
futuro.
Criando uma interface para os métodos principais de uma classe e
utilizando
esta interface para acessá-los torna mais fácil o
processo
de manutenção do código caso esta classe precise
ser
substituída posteriormente, como demonstrado no exemplo do collections
framework.
Além disso, interfaces também podem deixar seu
código
mais reusável, uma vez que uma única classe pode
trabalhar
com várias outras através de uma única interface.
No
caso do ResultSet
vimos que é possível beneficiar-se
de seus métodos sem ao menos saber que classe estamos usando.
Desta
forma, é mais fácil dividir a tarefa entre vários
desenvolvedores
de um projeto. Desde que todos respeitem as interfaces, a
integração
entre o trabalho de cada um ocorrerá sem grandes problemas.
No próprio modelo MVC, ter uma interface comum para as actions
da camada model facilita muito o desenvolvimento do controller.
Cada action implementa uma interface em comum, e então o
controller
chama os métodos diretamente dela.
E depois para utilizar o endereço em outra classe basta colocar Enderecos.alemDoJava ou Enderecos.portalJava.
Conclusões
Interfaces são uma ferramenta importante da linguagem Java,
melhorando
a qualidade do seu código quando bem utilizadas.
Elas permitem um encapsulamento de comportamento, ocultando qual classe
está
realizando uma tarefa específica. Isso é possível
porque
a classe que implementa uma interface é obrigada a seguir o
protocolo
definido nesta.
Desta forma, as principais vantagens conseguidas através de seu
uso
são uma manutenção mais simples do código e
uma
maior reusabilidade do mesmo, aproveitando melhor os benefícios
da
programação orientada a objetos.
Vanessa
Sabino