Você talvez já tenha pensado, se a partir do Java 8 podemos ter métodos default e/ou static em interfaces, qual a diferença entre classes abstratas e interfaces?
Além de uma usar a palavra chave extends e outra implements, podemos considerar 3 diferenças, são elas:
- Tipos de atributos
- Visibilidade de métodos
- Herança e abstração
Tipos de atributos
Em interfaces todo atributo tem por padrão os modificadores static, final e public, ou seja todo atributo será uma constante publica. Caso você tente forçar modificadores diferentes será gerado um erro de compilação. Já nas classes abstratas, é possível utilizar outros modificadores assim como em classes não abstratas.
Visibilidade de métodos
Em interfaces todo método deve ser publico. Mesmo que você não inclua o modificador public, caso você tente forçar outro tipo de visibilidade isso resultará em um erro. Já as classes abstratas você além de por padrão os métodos terem visibilidade default, é possível explicitamente definir qualquer tipo de visibilidade para os métodos, desde que ele não seja abstrato (métodos abstratos não podem ser privados, pois indiretamente isso os tornaria finais, já que não poderiam ser sobrescritos).
Java 9+
A partir do Java 9, interfaces podem ter métodos privados. Lembrando que métodos privados não podem ser abstract ou default. Não faria sentido, visto que métodos privados não são vistos pelas implementações ou qualquer outra classe ou interface.
Herança e abstração
Classes abstratas podem ser estendidas (usando palavra chave extends) por outras classes enquanto que interfaces podem ser implementadas (palavra chave implements). A grande diferença nesse caso é quantitativa, é possível estender de forma direta apenas uma classe, enquanto que é possível implementar diversas interfaces.
A limitação de classes que você pode estender se aplica apenas a herança de forma direta. Uma Classe Poodle pode estender da classe Cachorro que pode estender de Animal, como pode ser visto no exemplo abaixo.
abstract class Animal {
private Date dataNascimento;
private String nome;
abstract public void emitirSom();
//geters e setters
}
class Cachorro extends Animal {
@Override
public void emitirSom(){
//implementação
}
}
class Poodle extends Cachorro {
public void tosar(){
//implementação
}
}
No exemplo acima a classe Poodle estende diretamente apenas de Cachorro conforme a especificação, porém indiretamente também estende de Animal, o que é permitido.
Outro detalhe sobre o exemplo é que é possível também estender de classes não abstratas. Um exemplo é a classe cachorro que intencionalmente não é abstrata, mas ainda assim pode ser estendida pela classe Poodle.