Interfaces y clases abstractas de confusión en Java con ejemplos.

Tengo problemas para entender cuándo usar una interfaz en lugar de una clase abstracta y viceversa. Además, estoy confundido cuando extender una interfaz con otra interfaz. Lo siento por el largo post, pero esto es muy confuso.

Crear formas parece ser un punto de partida popular. Digamos que queremos una forma de modelar formas 2D. Sabemos que cada forma tendrá un área. ¿Cuál sería la diferencia entre las siguientes dos implementaciones?

con interfaces:

public interface Shape {
    public double area();
}

public class Square implements Shape{
    private int length = 5;
    public Square(){...}

    public double area()
         return length * length;
    }
}

con clase abstracta:

abstract class Shape {
    abstract public double area();
}

public class Square extends Shape {
    private length = 5;
    public Square(){...}

    public double area(){
        return length * length;
    }

Entiendo que las clases abstractas le permiten definir variables de instancia y le permite dar implementaciones de métodos mientras que una interfaz no puede hacer estas cosas. Pero en este caso, parece que estas dos implementaciones son idénticas. ¿Así que usar cualquiera está bien?

Pero ahora digamos que queremos describir diferentes tipos de triángulos. Podemos tener triángulos isósceles, agudos y de ángulo recto. Para mí, tiene sentido usar la herencia de clase en este caso. Usando la definición 'IS-A': un triángulo rectángulo "IS-A" triángulo. Una forma de triángulo "IS-A". Además, una clase abstracta debe definir comportamientos y atributos que son comunes en todas las subclases, por lo que esto es perfecto:

con clase abstracta

abstract Triangle extends Shape {
    private final int sides = 3;
}
class RightTriangle extends Triangle {
    private int base = 4;
    private int height = 5;

    public RightTriangle(){...}

    public double area() {
        return .5 * base * height
    }
}

También podemos hacer esto con las interfaces, ya que Triangle y Shape son interfaces. Sin embargo, a diferencia de la herencia de clase (utilizando la relación 'IS-A' para definir lo que debería ser una subclase), no estoy seguro de cómo usar una interfaz. Veo dos maneras:

Primera forma:

  public interface Triangle {
      public final int sides = 3;
  }
  public class RightTriangle implements Triangle, Shape {
      private int base = 4;
      private int height = 5;

      public RightTriangle(){}
      public double area(){
          return .5 * height * base;
      }
  }

Segunda forma:

public interface Triangle extends Shape {
     public final int sides = 3;
} 
public class RightTriangle implements Triangle {
    ....

    public double area(){
         return .5 * height * base;
    }
}

Me parece que ambas formas funcionan. Pero, ¿cuándo usarías una manera sobre la otra? ¿Y hay ventajas al usar interfaces sobre clases abstractas para representar diferentes triángulos? A pesar de que complicamos la descripción de una forma, usar interfaz vs clase abstracta parece equivalente.

Un componente crítico de las interfaces es que puede definir comportamientos que pueden compartirse entre clases no relacionadas. Por lo tanto, una interfaz Flyable estaría presente en las clases Airplane, así como en Bird. Entonces, en este caso, está claro que se prefiere un enfoque de interfaz.

Además, para construir fuera de la interfaz confusa extendiendo otra interfaz: ¿Cuándo debe ignorarse la relación 'IS-A' cuando se decide qué debe ser una interfaz? Tomemos este ejemplo:ENLAZAR.

¿Por qué debería ser 'VeryBadVampire' una clase y 'Vampire' ser una interfaz? Un 'VeryBadVampire' ES-A 'Vampire', por lo que entiendo que un 'Vampire' debería ser una superclase (tal vez clase abstracta). Una clase de 'Vampiro' puede implementar 'Lethal' para mantener su comportamiento letal. Además, un 'Vampiro' IS-A 'Monstruo', así que 'Monstruo' debería ser una clase también. Una clase 'Vampiro' también puede implementar una interfaz llamada 'Peligrosa' para mantener su comportamiento peligroso. Si deseamos crear un nuevo monstruo llamado 'BigRat' que sea peligroso pero no letal, entonces podemos crear una clase 'BigRat' que extienda 'Monster' e implemente 'Dangerous'.

¿No lograría lo anterior la misma salida que usando 'Vampire' como interfaz (descrito en el enlace)? La única diferencia que veo es que usar la herencia de clase y preservar la relación 'IS-A' aclara mucha confusión. Sin embargo, esto no se sigue. ¿Cuál es la ventaja de hacer esto?

Incluso si quisieras que un monstruo compartiera el comportamiento vampírico, uno siempre puede redefinir cómo se representan los objetos. Si quisiéramos un nuevo tipo de monstruo vampiro llamado 'VeryMildVampire' y quisiéramos crear un monstruo parecido a un vampiro llamado 'Chupacabra', podemos hacer esto:

La clase 'Vampire' extiende 'Monster' implementa 'Dangerous', 'Lethal', 'BloodSuckable'
La clase 'VeryMildVampire' extiende la clase 'Vampire'
La clase 'Chupacabra' extiende los implementos 'Monster' 'BloodSuckable'

Pero también podemos hacer esto:

'VeryMildVampire' extiende los implementos 'Monster' Peligroso, letal, vampírico
'Chupacabra' extiende los implementos 'Monster' Peligroso, Vampírico

La segunda forma aquí crea una interfaz 'Vampírica' para que podamos definir más fácilmente un monstruo relacionado en lugar de crear un grupo de interfaces que definen comportamientos vampíricos (como en el primer ejemplo). Pero esto rompe la relación IS-A. Así que estoy confundido ...

Respuestas a la pregunta(7)

Su respuesta a la pregunta