Por que uma classe abstrata que implementa uma interface pode perder a declaração / implementação de um dos métodos da interface?

123

Uma coisa curiosa acontece em Java quando você usa uma classe abstrata para implementar uma interface: alguns dos métodos da interface podem estar completamente ausentes (ou seja, não existe uma declaração abstrata ou uma implementação real), mas o compilador não reclama.

Por exemplo, dada a interface:

public interface IAnything {
  void m1();
  void m2();
  void m3();
}

a seguinte classe abstrata é alegremente compilada sem aviso ou erro:

public abstract class AbstractThing implements IAnything {
  public void m1() {}
  public void m3() {}
}

Você pode explicar o porquê?

Giulio Piancastelli
fonte
2
Não se pode criar um objeto de uma classe abstrata. Portanto, desde que uma implementação não seja fornecida para uma classe abstrata, os objetos não poderão ser criados para o IAnything. Portanto, isso é absolutamente bom para o compilador. O compilador espera que qualquer classe não abstrata que implemente o IAnything deve implementar todos os métodos declarados fora do IAnything. E como é necessário estender e implementar o AbstractThing para criar objetos, o compilador emitirá um erro, se essa implementação não implementar os métodos de IAnything deixados de fora pelo AbstractThing.
Vanagas
Eu tinha uma classe concreta que estava estendendo seu próprio "AbstractThing" em um cenário idêntico a esse e, embora não tivesse implementado um dos métodos na interface, ele estava inexplicavelmente compilando. Agora está fazendo o que eu esperava, mas não consigo descobrir o que estava causando o sucesso antes. Eu suspeito que não tinha :wum dos arquivos.
Braden Best
você pode ver a resposta para uma pergunta semelhante stackoverflow.com/questions/8026580/…
Do Nhu Vy

Respostas:

155

Isso ocorre porque, se uma classe é abstrata, por definição você é obrigado a criar subclasses para instanciar. As subclasses serão necessárias (pelo compilador) para implementar quaisquer métodos de interface que a classe abstrata tenha deixado de fora.

Seguindo o código de exemplo, tente criar uma subclasse AbstractThingsem implementar o m2método e veja quais erros o compilador fornece. Isso forçará você a implementar esse método.

Bill the Lizard
fonte
1
Eu acho que o compilador ainda deve lançar avisos sobre classes abstratas que implementam interfaces de maneira incompleta, simplesmente porque você precisa examinar 2 definições de classe em vez de 1 para ver o que você precisa em uma subclasse. Esta é uma limitação de idioma / compilador.
workmad3
3
Isso não seria uma boa ideia, pois normalmente pode haver muitas classes abstratas e os avisos 'falsos' logo o sobrecarregarão, fazendo com que você perca os avisos 'verdadeiros'. Se você pensar bem, a palavra-chave 'abstract' está lá especificamente para dizer ao compilador para suprimir avisos para essa classe.
Belugabob 13/10/08
4
@workmad - se você tem implementações comuns para um subconjunto de métodos de interface que faz mais sentido fator-lo em uma classe base separada (DRY supera um lugar-code)
Gishu
4
Seria perigoso exigir que você coloque implementações de métodos vazias em uma classe abstrata. Se você fez isso, os implementadores de subclasses herdariam esse não comportamento sem que o compilador dissesse a eles que havia um problema.
Bill o Lagarto
8
Acho que o workmad pode estar sugerindo é que você defina os métodos na classe abstrata sem um corpo de método e os marque abstratos. Não me parece uma má ideia.
Dónal 13/10/08
33

Perfeitamente bem.
Você não pode instanciar classes abstratas .. mas as classes abstratas podem ser usadas para hospedar implementações comuns para m1 () e m3 ().
Portanto, se a implementação de m2 () for diferente para cada implementação, mas m1 e m3 não. Você pode criar diferentes implementações concretas de IAnything apenas com a implementação de m2 diferente e derivar do AbstractThing - honrando o princípio DRY. Validar se a interface está completamente implementada para uma classe abstrata é inútil.

Atualização : Curiosamente, acho que o C # impõe isso como um erro de compilação. Você é forçado a copiar as assinaturas do método e prefixá-las com 'abstract public' na classe base abstrata neste cenário. (Algo novo todos os dias :)

Gishu
fonte
7

Isso é bom. Para entender o que foi dito acima, primeiro é necessário entender a natureza das classes abstratas. Eles são semelhantes às interfaces a esse respeito. É o que a Oracle diz sobre isso aqui .

Classes abstratas são semelhantes às interfaces. Você não pode instancia-los e eles podem conter uma mistura de métodos declarados com ou sem uma implementação.

Então você tem que pensar no que acontece quando uma interface estende outra interface. Por exemplo ...

//Filename: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

//Filename: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

... como você pode ver, isso também compila perfeitamente. Simplesmente porque, assim como uma classe abstrata, uma interface NÃO pode ser instanciada. Portanto, não é necessário mencionar explicitamente os métodos do seu "pai". No entanto, TODAS as assinaturas do método pai tornam-se implicitamente parte da interface de extensão ou implementação da classe abstrata. Portanto, uma vez que uma classe apropriada (uma que possa ser instanciada) estenda o descrito acima, será necessário garantir que todos os métodos abstratos sejam implementados.

Espero que ajude ... e Allahu 'alam!

Grato
fonte
Esse é um ponto de vista interessante. Isso me faz pensar que "classes abstratas" são realmente "interfaces concretas", isto é, interfaces com alguns métodos concretos, em vez de classes com alguns métodos abstratos.
Giulio Piancastelli
... um pouco de ambos realmente. Mas uma coisa é certa, eles não são instantâneos.
Grateful
4

Interface significa uma classe que não possui implementação de seu método, mas apenas com declaração.
Por outro lado, a classe abstrata é uma classe que pode ter implementação de algum método, juntamente com algum método com apenas declaração, sem implementação.
Quando implementamos uma interface para uma classe abstrata, isso significa que a classe abstrata herdou todos os métodos da interface. Como não é importante implementar todo o método na classe abstrata, no entanto, trata-se da classe abstrata (também por herança), portanto a classe abstrata pode deixar parte do método na interface sem implementação aqui. Porém, quando essa classe abstrata será herdada por alguma classe concreta, eles deverão implementar todos os métodos não implementados na classe abstrata.

Mustakimur Rahman
fonte
4

Dada a interface:

public interface IAnything {
  int i;
  void m1();
  void m2();
  void m3();
}

É assim que o Java realmente o vê:

public interface IAnything {
  public static final int i;
  public abstract void m1();
  public abstract void m2();
  public abstract void m3();
}

Portanto, você pode deixar alguns (ou todos) esses abstractmétodos não implementados, exatamente como faria no caso de abstractclasses estendendo outra abstractclasse.

Quando implementum interface, a regra de que todos os interfacemétodos devem ser implementados na derivada class, aplica-se apenas ao concreto classde execução (ou seja, que não é abstractem si).

Se você realmente planeja criar uma abstract classsaída, não há uma regra que diga a você implementtodos os interfacemétodos (observe que, nesse caso, é obrigatório declarar a derivada classcomo abstract)

sharhp
fonte
Usando javap IAnything.classpara gerar o segundo trecho de código.
sharhp
3

Quando uma classe abstrata implementa uma interface

Na seção Interfaces, observou-se que uma classe que implementa uma interface deve implementar todos os métodos da interface. É possível, no entanto, definir uma classe que não implemente todos os métodos da interface, desde que a classe seja declarada abstrata. Por exemplo,

abstract class X implements Y {   
    // implements all but one method of Y
}

class XX extends X {   
    // implements the remaining method in Y 
} 

Nesse caso, a classe X deve ser abstrata porque não implementa completamente Y, mas a classe XX, de fato, implementa Y.

Referência: http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

Do Nhu Vy
fonte
1

Classes abstratas não são necessárias para implementar os métodos. Portanto, mesmo que implemente uma interface, os métodos abstratos da interface podem permanecer abstratos. Se você tentar implementar uma interface em uma classe concreta (por exemplo, não abstrata) e não implementar os métodos abstratos, o compilador lhe dirá: Implemente os métodos abstratos ou declare a classe como abstrata.

Vincent Ramdhanie
fonte