Interfaces em Java
Em Java, as interfaces são definidas de maneira similar às classes. Normalmente, as interfaces não contém atributos ou métodos, embora recentemente tenha sido adicionado suporte para métodos padrão (default), que já têm uma implementação pré-definida na interface. Desta forma, as classes que implementam a interface podem decidir se modificam ou não o método padrão.
Uma interface é definida pela palavra chave interface e pode, por sua
vez, herdar de outras interfaces. Para interfaces é permitida a herança
múltipla (a interface List, que representa listas em Java, por
exemplo, estende as interfaces Collection e Iterable, que
representam coleções e elementos iteráveis, respectivamente).
O exemplo a seguir mostra a definição de uma interface chamada IConta
(linha ), que define que
as classes que a implementam devem conter a implementação de ao menos
dois métodos: sacar e transferir (linhas
e
,
respectivamente).
public interface IConta{
public void sacar(Double valor);
public void transferir(Conta destino, Double valor);
}
Ao implementarmos a interface IConta na classe Conta, todas as
subclasses de Conta herdam os métodos e, desta forma, estão de acordo
com a interface. O exemplo a seguir mostra a declaração que define que
Conta implementa IConta (linha
) e a
implementação dos métodos sacar e transferir (linhas
e
,
respectivamente). Neste exemplo específico, estamos implementando os
métodos da interface em uma classe Conta concreta. Desta forma, as
subclasses de Conta não precisam implementar esses dois métodos.
public class Conta implements IConta{
private Double saldo;
public Double getSaldo(){
return saldo;
}
public void setSaldo(Double saldo){
this.saldo = saldo;
}
// Implementação concreta de sacar
public void sacar(Double valor){
setSaldo(getSaldo() - valor);
}
public void depositar(Double valor){
setSaldo(getSaldo() + valor);
}
// Implementação concreta de transferir
public void transferir(Conta destino, Double valor){
this.sacar(valor);
destino.depositar(valor);
}
}
Outra alternativa seria mover a implementação dos métodos sacar,
depositar e transferir para a interface IConta, usando métodos
default. Para isso, é necessário declarar também os métodos getSaldo
e setSaldo na interface:
public interface IConta{
public Double getSaldo();
public void setSaldo(Double saldo);
public default void sacar(Double valor){
setSaldo(getSaldo() - valor);
}
public default void transferir(IConta destino,
Double valor) {
destino.depositar(valor);
this.sacar(valor);
}
public default void depositar(Double valor){
setSaldo(getSaldo() + valor);
}
}
E a classe Conta simplificada ficaria:
public class Conta implements IConta{
private Double saldo;
public Double getSaldo(){
return saldo;
}
public void setSaldo(Double saldo){
this.saldo = saldo;
}
public Conta(Double valor){
setSaldo(valor);
}
}
Desacoplar a interface da Conta pode dar mais flexibilidade às classes
em relação à forma pela qual o saldo é armazenado e manipulado. Por
exemplo, no framework de coleções de Java existe a interface List que
define o que uma lista deve fazer. As implementações concretas de
List, tais como ArrayList e LinkedList fornecem diferentes
estratégias de armazenamento e acesso: a primeira usa um vetor para
armazenar os dados, enquanto a segunda usa uma lista duplamente
encadeada.
Este artigo é uma adaptação de conteúdo do livro Orientação a Objetos com Java, de Eduardo Kessler Piveta.