JavaRush /Blogue Java /Random-PT /Pausa para café #140. Classes abstratas e interfaces em J...

Pausa para café #140. Classes abstratas e interfaces em Java

Publicado no grupo Random-PT
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. Pausa para café #140.  Classes abstratas e interfaces em Java - 1Classes 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.
Muitos desenvolvedores pensam que interfaces e classes abstratas são semelhantes, mas na realidade isso não é inteiramente verdade. Vejamos as principais diferenças entre eles.

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).
Aulas abstratas:
  • 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.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION