¿Por qué una clase abstracta que implementa una interfaz puede perder la declaración / implementación de uno de los métodos de la interfaz?

123

Al usar una clase abstracta para implementar una interfaz, sucede algo curioso en Java: algunos de los métodos de la interfaz pueden faltar por completo (es decir, no hay una declaración abstracta o una implementación real), pero el compilador no se queja.

Por ejemplo, dada la interfaz:

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

la siguiente clase abstracta se compila alegremente sin advertencia o error:

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

¿Puedes explicar porque?

Giulio Piancastelli
fuente
2
No se puede crear un objeto de una clase abstracta. Por lo tanto, siempre que no se proporcione una implementación para una clase abstracta, no se pueden crear objetos para IAnything. Entonces esto está absolutamente bien para el compilador. El compilador espera que cualquier clase no abstracta que implemente IAnything debe implementar todos los métodos declarados fuera de IAnything. Y dado que uno tiene que extender e implementar AbstractThing para poder crear objetos, el compilador arrojará un error, si esa implementación no implementa los métodos de IAnything omitidos por AbstractThing.
VanagaS
Tenía una clase concreta que estaba extendiendo su propio "AbstractThing" en un escenario idéntico a este, y aunque no había implementado uno de los métodos en la interfaz, estaba compilando inexplicablemente. Ahora está haciendo lo que espero, pero no puedo entender qué estaba causando que tuviera éxito antes. Sospecho que no tenía :wuno de los archivos.
Braden Best
puede ver la respuesta para una pregunta similar stackoverflow.com/questions/8026580/…
Do Nhu Vy

Respuestas:

155

Esto se debe a que si una clase es abstracta, entonces, por definición, debe crear subclases para crear una instancia. Se requerirán las subclases (por parte del compilador) para implementar cualquier método de interfaz que la clase abstracta haya omitido.

Siguiendo su código de ejemplo, intente hacer una subclase de AbstractThingsin implementar el m2método y vea qué errores le da el compilador. Te obligará a implementar este método.

Bill el lagarto
fuente
1
Creo que el compilador aún debería lanzar advertencias con respecto a las clases abstractas que implementan interfaces de manera incompleta, simplemente porque entonces debe ir a través de 2 definiciones de clase en lugar de 1 para ver lo que necesita en una subclase. Sin embargo, esta es una limitación de idioma / compilador.
workmad3
3
Esa no sería una buena idea, ya que normalmente puede haber muchas clases abstractas y las advertencias 'falsas' pronto lo abrumarían, haciendo que se pierda las advertencias 'verdaderas'. Si lo piensa, la palabra clave 'abstracta' está allí específicamente para decirle al compilador que suprima las advertencias para esa clase.
belugabob
44
@workmad: si tiene implementaciones comunes para un subconjunto de métodos de interfaz, tiene más sentido factorizarlo en una clase base separada (DRY triunfa sobre el código de un solo lugar)
Gishu
44
Sería peligroso requerirle que ponga implementaciones de métodos vacías en una clase abstracta. Si lo hiciera, los implementadores de las subclases heredarían esta falta de comportamiento sin que el compilador les dijera que hay un problema.
Bill the Lizard
8
Creo que workmad podría estar sugiriendo que defina los métodos en la clase abstracta sin un cuerpo de método y los marque como abstractos. No me parece una mala idea.
Dónal
33

Perfectamente bien.
No puede crear instancias de clases abstractas ... pero las clases abstractas pueden usarse para albergar implementaciones comunes para m1 () y m3 ().
Entonces, si la implementación m2 () es diferente para cada implementación pero m1 y m3 no lo son. Puede crear diferentes implementaciones concretas de IAnything con solo la implementación de m2 diferentes y derivar de AbstractThing, respetando el principio DRY. Validar si la interfaz está completamente implementada para una clase abstracta es inútil.

Actualización : Curiosamente, encuentro que C # impone esto como un error de compilación. En este escenario, se ve obligado a copiar las firmas del método y agregarles el prefijo 'public abstract' en la clase base abstracta ... (algo nuevo todos los días :)

Gishu
fuente
7

Esta bien. Para comprender lo anterior, primero debe comprender la naturaleza de las clases abstractas. Son similares a las interfaces a ese respecto. Esto es lo que Oracle dice sobre esto aquí .

Las clases abstractas son similares a las interfaces. No puede crear instancias de ellos, y pueden contener una combinación de métodos declarados con o sin implementación.

Por lo tanto, debe pensar en lo que sucede cuando una interfaz extiende otra interfaz. Por ejemplo ...

//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 puede ver, esto también se compila perfectamente bien. Simplemente porque, al igual que una clase abstracta, una interfaz NO puede ser instanciada. Por lo tanto, no es necesario mencionar explícitamente los métodos de su "padre". Sin embargo, TODAS las firmas del método padre se convierten implícitamente en parte de la interfaz extendida o la implementación de la clase abstracta. Por lo tanto, una vez que una clase adecuada (una que se puede instanciar) extiende lo anterior, será necesario asegurarse de que se implemente cada método abstracto.

Espero que ayude ... y Allahu 'alam!

Agradecido
fuente
Ese es un punto de vista interesante. Me hace pensar que las "clases abstractas" son realmente "interfaces concretas", es decir, interfaces con algunos métodos concretos, en lugar de clases con algunos métodos abstractos.
Giulio Piancastelli
... Un poco de ambos realmente. Pero una cosa es segura, no son instanciables.
Agradecido
4

Interfaz significa una clase que no tiene implementación de su método, pero solo con una declaración.
Por otro lado, la clase abstracta es una clase que puede tener implementación de algún método junto con algún método con solo declaración, sin implementación.
Cuando implementamos una interfaz para una clase abstracta, significa que la clase abstracta heredó todos los métodos de la interfaz. Como, no es importante implementar todo el método en la clase abstracta, sin embargo, se trata de la clase abstracta (también por herencia), por lo que la clase abstracta puede dejar parte del método en la interfaz sin implementación aquí. Pero, cuando esta clase abstracta sea heredada por alguna clase concreta, deben implementar todos los métodos no implementados en la clase abstracta.

Mustakimur Rahman
fuente
4

Dada la interfaz:

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

Así es como Java lo ve realmente:

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

Por lo tanto, puede dejar algunos (o todos) de estos abstractmétodos sin implementar, tal como lo haría en el caso de las abstractclases que extienden otra abstractclase.

Cuando implementuna interface, la regla de que todos los interfacemétodos deben ser implementadas en la derivada class, sólo se aplica al hormigón classaplicación (es decir, que no es abstracten sí mismo).

Si de hecho planea crear un abstract classfuera de él, entonces no hay una regla que diga que tiene implementtodos los interfacemétodos (tenga en cuenta que en tal caso es obligatorio declarar el derivado classcomo abstract)

sharhp
fuente
Utilizando javap IAnything.classpara generar el segundo fragmento de código.
sharhp
3

Cuando una clase abstracta implementa una interfaz

En la sección sobre Interfaces, se observó que una clase que implementa una interfaz debe implementar todos los métodos de la interfaz. Sin embargo, es posible definir una clase que no implemente todos los métodos de la interfaz, siempre que se declare que la clase es abstracta. Por ejemplo,

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

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

En este caso, la clase X debe ser abstracta porque no implementa completamente Y, pero la clase XX, de hecho, implementa Y.

Referencia: http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

Do Nhu Vy
fuente
1

No se requieren clases abstractas para implementar los métodos. Entonces, aunque implemente una interfaz, los métodos abstractos de la interfaz pueden seguir siendo abstractos. Si intenta implementar una interfaz en una clase concreta (es decir, no abstracta) y no implementa los métodos abstractos, el compilador le dirá: Implemente los métodos abstractos o declare la clase como abstracta.

Vincent Ramdhanie
fuente