Fonte: InfoWorld Hoje você aprenderá em quais casos um desenvolvedor deve usar uma classe abstrata e em quais casos uma interface. Também identificaremos as diferenças entre esses elementos da linguagem Java e como utilizá-los em programas. Classes e interfaces abstratas são bastante comuns no código Java e até mesmo no próprio Java Development Kit (JDK). Cada um desses elementos tem uma finalidade diferente:
- Uma interface é uma construção na linguagem Java que ajuda a implementar métodos abstratos e constantes estáticas.
- As classes abstratas são semelhantes às classes regulares, exceto que podem incluir métodos abstratos, ou seja, métodos sem corpo. Classes abstratas não podem ser criadas.
O que é uma interface
Em sua essência, uma interface é um contrato, portanto depende da implementação para definir o propósito de sua criação. A interface não pode usar variáveis de instância mutáveis, ela só pode usar variáveis finais.Quando usar interfaces
As interfaces são muito úteis para separar código e implementar polimorfismo. Podemos ver isso no JDK com a interface List :public interface List<E> extends Collection<E> {
int size();
boolean isEmpty();
boolean add(E e);
E remove(int index);
void clear();
}
Como você provavelmente notou, este código, embora breve, é bastante descritivo. Podemos ver facilmente a assinatura do método que será usada para implementar os métodos na interface usando a classe concreta. A interface List contém um contrato que pode ser implementado por ArrayList , Vector , LinkedList e outras classes. Para usar o polimorfismo, podemos simplesmente declarar o tipo da nossa variável usando List e então selecionar qualquer uma das instâncias disponíveis. Aqui está outro exemplo:
List list = new ArrayList();
System.out.println(list.getClass());
List list = new LinkedList();
System.out.println(list.getClass());
A saída é:
classe java.util.ArrayList classe java.util.LinkedList
Nesse caso, os métodos de implementação de ArrayList , LinkedList e Vector são diferentes, o que é um excelente cenário para utilização da interface. Se você notar que muitas classes pertencem a uma classe pai com as mesmas ações de método, mas com comportamento diferente. Nessas situações, é recomendável utilizar a interface. A seguir, veremos várias opções de uso de interfaces.
Substituição de método de interface
Como já sabemos, uma interface é uma espécie de contrato que deve ser implementado por uma classe concreta. Os métodos de interface são implicitamente abstratos e requerem uma implementação concreta da classe. Aqui está um exemplo:public class OverridingDemo {
public static void main(String[] args) {
Challenger challenger = new JavaChallenger();
challenger.doChallenge();
}
}
interface Challenger {
void doChallenge();
}
class JavaChallenger implements Challenger {
@Override
public void doChallenge() {
System.out.println("Challenge done!");
}
}
Conclusão:
Desafio concluído!
Observe que os métodos de interface são implicitamente abstratos. Isso significa que não precisamos declará-los explicitamente como abstratos.
Variáveis Constantes
Outra regra a lembrar é que uma interface só pode conter variáveis constantes. Aqui está um exemplo:public class Challenger {
int number = 7;
String name = "Java Challenger";
}
Aqui ambas as variáveis são implícitas final e static . Isso significa que eles são constantes, independentes da instância e não podem ser alterados. Agora vamos tentar alterar as variáveis na interface do Challenger , digamos assim:
Challenger.number = 8;
Challenger.name = "Another Challenger";
Isso causará um erro de compilação:
Não é possível atribuir um valor à variável final 'número' Não é possível atribuir um valor à variável final 'nome'
Métodos padrão
Quando os métodos padrão foram introduzidos no Java 8, alguns desenvolvedores pensaram que seriam iguais às classes abstratas. No entanto, isso não é verdade porque as interfaces não podem ter estado. Um método padrão pode ter uma implementação, mas os métodos abstratos não. Os métodos padrão são o resultado de inovações com expressões e fluxos lambda, mas devemos usá-los com cuidado. O método no JDK que usa o método padrão é forEach() , que faz parte da interface Iterable . Em vez de copiar o código em cada implementação Iterable , podemos simplesmente reutilizar o método forEach :default void forEach(Consumer<? super T> action) {
// Code implementation here...
Qualquer implementação Iterable pode usar o método forEach() sem exigir uma nova implementação de método. Podemos então reutilizar o código com o método padrão. Vamos criar nosso próprio método padrão:
public class DefaultMethodExample {
public static void main(String[] args) {
Challenger challenger = new JavaChallenger();
challenger.doChallenge();
}
}
class JavaChallenger implements Challenger { }
interface Challenger {
default void doChallenge() {
System.out.println("Challenger doing a challenge!");
}
}
Resultado:
Challenger fazendo um desafio!
Com relação aos métodos padrão, é importante observar que cada um desses métodos precisa ser implementado. O método padrão não pode ser estático. Agora vamos passar para as classes abstratas.
A essência de uma classe abstrata
Classes abstratas podem ter estado com variáveis de instância. Isso significa que a variável de instância pode ser usada e modificada. Aqui está um exemplo:public abstract class AbstractClassMutation {
private String name = "challenger";
public static void main(String[] args) {
AbstractClassMutation abstractClassMutation = new AbstractClassImpl();
abstractClassMutation.name = "mutated challenger";
System.out.println(abstractClassMutation.name);
}
}
class AbstractClassImpl extends AbstractClassMutation { }
Conclusão:
desafiante mutante
Métodos abstratos em classes abstratas
Assim como as interfaces, as classes abstratas podem ter métodos abstratos. Um método abstrato é um método sem corpo. Ao contrário das interfaces, os métodos abstratos em classes abstratas devem ser declarados explicitamente como abstratos. Aqui está um exemplo:public abstract class AbstractMethods {
abstract void doSomething();
}
E aqui está uma tentativa de declarar um método sem implementação e sem a palavra-chave abstrata :
public abstract class AbstractMethods {
void doSomethingElse();
}
Infelizmente, isso resulta em um erro de compilação:
Corpo do método ausente ou declaração abstrata
Quando usar classes abstratas
Uma classe abstrata é recomendada quando você precisa implementar um estado mutável. Por exemplo, o Java Collections Framework inclui uma classe AbstractList que usa o estado das variáveis. Nos casos em que você não precisa manter o estado da classe, geralmente é melhor usar uma interface.Diferenças entre classes abstratas e interfaces
Do ponto de vista da programação orientada a objetos, a principal diferença entre uma interface e uma classe abstrata é que uma interface não pode ter estado, enquanto uma classe abstrata pode ter estado com variáveis de instância. Outra diferença importante é que as classes podem implementar mais de uma interface, mas só podem estender uma classe abstrata. Esta solução é baseada no fato de que herança múltipla (estendendo mais de uma classe) pode levar a um impasse de código. Os desenvolvedores da linguagem Java decidiram evitar isso. Outra diferença é que as interfaces podem ser implementadas por classes ou estendidas por interfaces, mas as classes só podem ser estendidas. É importante observar que expressões lambda só podem ser usadas com uma interface funcional (ou seja, uma interface com apenas um método), enquanto classes abstratas com apenas um método abstrato não podem usar expressões lambda. Aqui estão mais algumas diferenças entre classes abstratas e interfaces. Interface:- Só pode ter variáveis estáticas finais. Uma interface nunca pode alterar seu próprio estado.
- Uma classe pode implementar múltiplas interfaces.
- Pode ser implementado usando a palavra-chave implements. Uma interface pode estender outra interface.
- Os métodos só podem usar campos finais estáticos, parâmetros ou variáveis locais.
- Somente interfaces funcionais podem usar a função lambda em Java.
- Não é possível ter um construtor.
- Pode ter métodos abstratos.
- Pode ter métodos padrão e estáticos (introduzidos no Java 8).
- Pode ter métodos privados com implementação (introduzido em Java 9).
- Pode ter qualquer instância ou variáveis estáticas, mutáveis ou imutáveis.
- Uma classe só pode estender uma classe abstrata.
- Pode ter uma instância de campos mutáveis, parâmetros ou variáveis locais.
- Classes abstratas com apenas um método abstrato não podem usar expressões lambda.
- Pode ter um construtor.
- Pode ter qualquer método.
GO TO FULL VERSION