Em Orientação a Objetos, o termo "interface" pode ter vários significados:
A interface gráfica de um sistema, ou seja, a camada visual de um sistema com a qual as pessoas que usam o sistema interagem.
Interface como forma de comunicação entre classes, por meio da criação de objetos, definição de atributos e chamada de métodos.
Interface como uma entidade orientada a objetos, que possui seu próprio conceito e que pode ser implementada por algumas linguagens orientadas a objetos (ex.: Java, C++). -> Este é o significado que será abordado neste conteúdo.
Significados do termo "Interface". Fonte: Autoral, feito com Canva para Educação.
Uma Interface é um contrato entre a interface (entidade) e outras classes do sistema (chamadas de classes clientes), no qual a interface define um conjunto de requisitos de comportamentos para que uma ou mais classes clientes possam implementá-la.
Quando uma ou mais classes implementam uma interface, elas se tornam classes clientes da interface e se comprometem a fornecer todos os comportamentos (métodos) definidos pela interface.
Arquivo: NomeInterface.java
public interface NomeDaInterface {
//conteúdo da interface
}
public é um modificador (opcional) de acesso que garante que todas as classes do sistema podem ver a interface descrita. Todas as interfaces são públicas por default;
Note que após o modificador de acesso public, a palavra-chave utilizada é interface e não class. Dessa forma, sabemos que se trata de uma interface e não de uma classe;
interface NomeInterface {} é a sintaxe para a declaração de uma interface. Assim como os arquivos de classes, o arquivo de uma Interface deve possuir o mesmo nome da interface: NomeDaInterface.java.
Arquivo: NomeClasseCliente.java
public class NomeClasseCliente implements NomeDaInterface {
//conteúdo da classe
}
implements é o termo utilizado por uma classe cliente para definir que esta classe implementa uma interface;
O termo implements deve ser seguido pelo nome de uma ou mais interfaces que serão implementadas pela classe. Quando houver mais de uma interface, os nomes das interfaces devem ser separados por vírgula.
ATENÇÃO: Por maiores que sejam as semelhanças, uma Interface não é uma Classe, ou seja, ela é, tecnicamente, um outro tipo de entidade. Por isto, uma interface em Java não pode ser instanciada e, consequentemente, não pode ter métodos construtores.
Relacionamento de implementação entre classes e interfaces. Fonte: Autoral, feito com Canva para Educação.
Interfaces tem como objetivo funcionar como um contrato, portanto servem para declarar constantes públicas e métodos que serão implementados por classes concretas.
Desta forma, uma interface pode ser composta por:
Atributos: Todo atributo em uma interface é implicitamente público, final e estático. Como esses modificadores/qualificadores são fixos por default, não é preciso declará-los. Ou seja, pode-se dizer que atributos em uma interface funcionam como "constantes globais".
Métodos abstratos: Interfaces possuem métodos abstratos que deverão ser implementados pelas classes concretas que implementam a interface, da mesma forma que em uma relação de herança. Todos os métodos de uma interface são implicitamente públicos (default) e abstratos (default).
Métodos concretos: Os métodos concretos de uma interface são herdados pelas classes que a implementam como em uma relação de herança. Como todos os métodos de uma interface são implicitamente públicos (default) e abstratos (default), é preciso apontar explicitamente a definição de um método concreto usando os modificadores static ou default.
Considerando estas características, interfaces são uma boa alternativa para suprir a necessidade semântica de herança múltipla.
Uma classe Java pode implementar uma ou mais interfaces (simulando uma herança múltipla).
Ao implementar uma interface, a classe cliente herda todas as relações e todos os componentes da interface, como em uma relação de herança comum.
Caso a classe cliente seja uma classe concreta, esta deve implementar também corpos para todos os métodos abstratos definidos na interface.
Caso a classe cliente seja uma classe abstrata, esta pode implementar os métodos abstratos da interface ou delegar a implementação destes métodos para as subclasses da sua hierarquia.
Quando uma classe é subclasse em uma relação de herança e também implementa uma ou mais interfaces, seja de forma direta ou indireta, ela se torna uma combinação de todos os tipos associados às suas relações:
seu próprio tipo;
o(s) tipo(s) da(s) classe(s) superior(es) da hierarquia de herança;
o(s) tipo(s) da(s) interface(s) que implementa diretamente, e;
o(s) tipo(s) da(s) interface(s) que implementa indiretamente via classe(s) superior(es) da hierarquia de herança.
Em outras palavras, qualquer objeto pode ser atribuído para um atributo do tipo de uma de suas superclasses ou para o tipo de uma das suas interfaces. Isso é um recurso extremamente poderoso, que promove reutilização e o desacoplamento de classes.
Exemplo de uma relação de herança com implementação de interfaces. Fonte: Autoral, feito com Canva para Educação.
Na sintaxe Java, isto significa que primeiro precisamos definir a herança (extends) e depois a implementação de interfaces (implements).
Arquivo: SuperclasseCliente.java
public class SuperclasseCliente implements InterfaceX{
//conteúdo da classe
}
Arquivo: ClasseCliente.java
public class ClasseCliente extends SuperclasseCliente implements NomeInterfaceY{
//conteúdo da classe
}
Arquivo: InterfaceX.java
public interface InterfaceX {
//conteúdo da interface X
//interfaces não podem ser instanciadas
}
Arquivo: SuperclasseCliente .java
public abstract class SuperclasseCliente implements InterfaceX{
//conteúdo da classe abstrata
//pode implementar ou não o corpo de todos os métodos da Interface X
//como é uma classe abstrata, não pode gerar instâncias
}
Arquivo: InterfaceY.java
public interface InterfaceY {
//conteúdo da interface Y
//interfaces não podem ser instanciadas
}
Arquivo: ClasseCliente.java
public class ClasseCliente extends SuperclasseCliente implements InterfaceY{
//conteúdo da classe concreta
//deve implementar o corpo para todos os métodos abstratos da Interface Y
//deve implementar o corpo para todos os métodos abstratos da Superclasse Cliente (se houver)
//deve implementar o corpo para todos os métodos abstratos da Interface X que não foram implementados ainda pela Superclasse Cliente (se houver)
//esta classe pode ser instanciada
}
Arquivo: Main.java
class Main {
public static void main(String[] args) {
ClasseCliente obj01 = new ClasseCliente();
SuperclasseCliente obj02 = new ClasseCliente();
InterfaceX obj03 = new ClasseCliente();
InterfaceY obj04 = new ClasseCliente();
}
}
Exemplo de Herança com Interfaces no domínio Animais. Fonte: Autoral, feito com Canva para Educação.
Arquivo LocomocaoAerea.java
interface LocomocaoAerea{
void voar(int metros);
}
Arquivo LocomocaoTerrestre.java
interface LocomocaoTerrestre{
void caminhar(int metros);
}
Arquivo LocomocaoAquatica.java
interface LocomocaoAquatica{
void nadar(int metros);
}
Arquivo Animal.java
public abstract class Animal{
}
Arquivo Ave.java
public abstract class Ave extends Animal{
}
Arquivo Mamifero.java
public abstract class Mamifero extends Animal{
}
Arquivo Peixe.java
public abstract class Peixe extends Animal implements LocomocaoAquatica{
}
Arquivo Pato.java
public class Pato extends Ave implements LocomocaoAerea, LocomocaoTerrestre, LocomocaoAquatica{
public void voar(int metros){
System.out.println("Voando...");
}
public void nadar(int metros){
System.out.println("Nadando...");
}
public void caminhar(int metros){
System.out.println("Caminhando...");
}
}
De forma semelhante à herança entre classes, uma interface pode estender outra interface definindo uma herança de interfaces.
A interface que estende outra interface é chamada de sub-interface ou interface filha e herda todos os componentes da interface estendida, também chamada de super-interface ou interface mãe.
A sintaxe utilizada é a mesma para a herança convencional, utilizando a palavra-chave extends.
Exemplo de Herança entre Interfaces no domínio Animais. Fonte: Autoral, feito com Canva para Educação.
Por se tratar de uma entidade diferenciada, interfaces em Java utilizam alguns modificadores, também chamados de qualificadores, especiais.
Nesta seção, descreve-se brevemente estes modificadores.
Podemos ter métodos concretos em uma interface. Para isso, devemos utilizar o modificador default.
Esses métodos serão herdados por todas as classes que implementarem a interface. Não sendo necessário implementá-los nas classes.
Métodos default podem ser sobrescritos.
Um método default precisa ser executado do contexto da instância de uma classe que implementa a interface. ex.: "p.pular();" sendo p uma instância da classe Pato.
Exemplo:
public interface LocomocaoTerrestre{
void caminhar(int metros);
default void pular(){
System.out.println("Pulando...");
}
}
Um método static é um método concreto (com corpo) que existe apenas no contexto da interface.
Assim, não é possível sobrescrever o método static.
Para executar um método estático, devemos referenciar a interface e não a classe ou o objeto que a implementa. Ex.: LocomocaoAquatica.motif();
Ou seja, é possível dizer que este tipo de método funciona como uma "função global".
Exemplo:
public interface LocomocaoAquatica{
void nadar(int metros);
static void motif(){
System.out.println("'Continue a nadar, nadar...'");
}
}
Um atributo static é um atributo compartilhado por um projeto e irá existir independente das instâncias de um objeto.
Ou seja, é possível dizer que este tipo de atributo funciona como uma "variável global".
Assim, em uma interface, um atributo estático pode ser referenciado no contexto da interface ou das instâncias que implementam a interface, seu valor também é compartilhado por todos os objetos. Ex.: LocomocaoAquatica.texto ou tubarao.texto.
Em uma interface, todo atributo é implicitamente estático.
Exemplo:
public interface LocomocaoAquatica{
void nadar(int metros);
String texto = "~~~";
}
Um atributo final é um atributo que não pode ter seu valor alterado. Ou seja, é possível dizer que este tipo de atributo funciona como uma constante.
Em uma interface, todo atributo é final.
Assim, todo atributo em uma interface é implicitamente público, final e estático, ou seja, está visível em todo o projeto, não pode ter seu valor alterado e o valor pode ser acessado por qualquer uma das instâncias que implementam a interface ou diretamente pela própria interface.