¡Hola! Hablemos de clases abstractas en Java.Ejemplos concretos de clases abstractas en Java - 1

¿Por qué las clases se llaman "abstractas"?

Probablemente recuerde qué es la abstracción; lo discutimos anteriormente :) Si lo olvidó, no se preocupe. Recuerde, es un principio de programación orientada a objetos que dice que, al diseñar clases y crear objetos, debe representar solo las propiedades principales de la entidad y descartar las secundarias. Por ejemplo, si estamos diseñando una SchoolTeacherclase, la altura probablemente no sea una propiedad necesaria de un maestro. De hecho, esta característica no es importante para un maestro. Pero si estamos creando una BasketballPlayerclase, la altura será una de las características más importantes. Bueno, una clase abstracta.es la "pieza de trabajo en bruto" más abstracta para un grupo de clases futuras. La pieza de trabajo no se puede usar directamente, es demasiado "áspera". Pero define cierto estado característico y comportamiento que tendrán las clases futuras, los descendientes de la clase abstracta.

Ejemplos de clases abstractas en Java

Considere un ejemplo simple con automóviles:

public abstract class Car {

   private String model;
   private String color;
   private int maxSpeed;
  
   public abstract void gas();

   public abstract void brake();

   public String getModel() {
       return model;
   }

   public void setModel(String model) {
       this.model = model;
   }

   public String getColor() {
       return color;
   }

   public void setColor(String color) {
       this.color = color;
   }

   public int getMaxSpeed() {
       return maxSpeed;
   }

   public void setMaxSpeed(int maxSpeed) {
       this.maxSpeed = maxSpeed;
   }
}
Así es como se ve la clase abstracta más simple. Como puede ver, nada especial :) ¿Por qué podríamos necesitar esto? Primero, proporciona la descripción más abstracta de la entidad que necesitamos: un automóvil. La palabra clave abstracta significa algo aquí. En el mundo real, no existe tal cosa como "solo un automóvil". Hay camiones, autos de carrera, sedanes, cupés y SUV. Nuestra clase abstracta es simplemente un "plano" que luego usaremos para crear clases de automóviles específicas.

public class Sedan extends Car {
  
   @Override
   public void gas() {
       System.out.println("The sedan is accelerating!");
   }

   @Override
   public void brake() {
       System.out.println("The sedan is slowing down!");
   }
  
}
En muchos sentidos, esto es similar a lo que hablamos en las lecciones sobre herencia. Solo que en ese caso teníamos una Carclase cuyos métodos no eran abstractos. Pero tal solución tiene varias desventajas que se solucionan en clases abstractas. En primer lugar, no se puede crear una instancia de una clase abstracta:

public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // Error! The Car class is abstract!
   }
}
El creador de Java hizo esta "característica" a propósito. Una vez más, recuerde: una clase abstracta es solo un modelo para futuras clases "regulares" . No necesitas copias de un plano, ¿verdad? Del mismo modo, no hay necesidad de crear instancias de una clase abstracta :) Y si la Carclase no fuera abstracta, podríamos crear fácilmente instancias de ella:

public class Car {

   private String model;
   private String color;
   private int maxSpeed;
  
   public void go() {
       // ...some logic
   }

   public  void brake() {
       // ...some logic
   }
}


public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // This is okay. The car is created.
   }
}
En la actualidad, nuestro programa tiene una especie de coche incomprensible. No es una camioneta, ni un auto de carrera, ni un sedán, pero no está muy claro qué es. Es el "solo un auto" que no existe en la realidad. El mismo ejemplo se puede dar con los animales. Imagínese si su programa tuviera Animalobjetos (" solo animales "). No está claro de qué tipo es, a qué familia pertenece o qué características tiene. Sería extraño ver uno en un programa. No hay "solo animales" en la naturaleza. Solo perros, gatos, zorros, topos, etc. Las clases abstractas nos salvan de " solo objetos ". Nos dan el estado de referencia y el comportamiento. Por ejemplo, todos los coches deben tener un modelo , color y velocidad máxima., y también deben poder aplicar el acelerador y el freno . Eso es todo. Es un modelo abstracto general que usará más adelante para diseñar las clases que necesita. Nota: los dos métodos de la clase abstracta también son abstractos , lo que significa que no tienen ninguna implementación. La razón es la misma: las clases abstractas no crean "comportamientos predeterminados" para "solo automóviles". Simplemente indican lo que cada coche debe ser capaz de hacer. Dicho esto, si necesita un comportamiento predeterminado, puede implementar métodos en una clase abstracta. Java no prohíbe esto:

public abstract class Car {

   private String model;
   private String color;
   private int maxSpeed;

   public void gas() {
       System.out.println("Accelerating!");
   }

   public abstract void brake();
  
   // Getters and setters
}


public class Sedan extends Car {

   @Override
   public void brake() {
       System.out.println("The sedan is slowing down!");
   }

}

public class Main {

   public static void main(String[] args) {

       Sedan sedan = new Sedan();
       sedan.gas();
   }
}
Salida de la consola: ¡
Accelerating!
Como puede ver, implementamos un método en la clase abstracta, pero no el otro. Como resultado, el comportamiento de nuestra Sedanclase se divide en dos partes: si llamamos a su gas()método, el comportamiento se "extrae" de la Carclase principal abstracta e implementamos el brake()método en la Sedanclase. Eso es súper conveniente y flexible. Pero nuestra clase no es tan abstracta ahora, ¿verdad ? Después de todo, en realidad implementó la mitad de los métodos. El hecho es, y esta es una característica muy importante, una clase es abstracta si incluso uno de sus métodos es abstracto.. Un método de dos, o uno de mil, no importa. Incluso podríamos implementar todos los métodos, sin dejar ninguno abstracto. El resultado sería una clase abstracta sin ningún método abstracto. Esto es posible en principio, el compilador no generará ningún error, pero es mejor no hacerlo, ya que priva a la palabra resumen de su significado. Tus compañeros programadores también se sorprenderán al ver esto :/ Dicho esto, si un método está marcado como abstracto, cada clase descendiente debe implementarlo o declararse como abstracto. De lo contrario, el compilador arrojará un error.. Por supuesto, cada clase solo puede heredar una clase abstracta, por lo que no hay diferencia entre las clases abstractas y regulares en términos de herencia. No importa si heredamos una clase abstracta o una regular, solo puede haber una clase principal.

¿Por qué Java no tiene herencia de clases múltiples?

Ya hemos dicho que no hay herencia múltiple en Java, pero realmente no profundizamos en por qué. Tratemos de hacer eso ahora. El hecho es que si Java tuviera herencia múltiple, las clases secundarias no podrían decidir qué comportamiento elegir. Digamos que tenemos dos clases: Toastery NuclearBomb:

public class Toaster {
  
  
 public void on() {

       System.out.println("The toaster is on. We're toasting!");
   }
  
   public void off() {

       System.out.println("The toaster is off!");
   }
}


public class NuclearBomb {

   public void on() {

       System.out.println("Boom!");
   }
}
Como puede ver, ambas clases tienen un on()método. Para la tostadora, el método comienza haciendo tostadas, pero en el caso de la bomba nuclear, provoca una explosión. Uh-oh :/ Ahora imagina que has decidido (¡no me preguntes por qué!) crear algo intermedio. Aquí está tu clase: MysteriousDevice! Este código no funcionará, por supuesto. Lo presentamos simplemente como un ejemplo de "lo que podría haber sido":

public class MysteriousDevice extends Toster, NuclearBomb {

   public static void main(String[] args) {
      
       MysteriousDevice mysteriousDevice = new MysteriousDevice();
       mysteriousDevice.on(); // And what should happen here? Will we get toast or a nuclear apocalypse?
   }
}
Veamos lo que tenemos. El misterioso dispositivo deriva de Toaster y NuclearBomb al mismo tiempo. Ambos tienen un on()método. Como resultado, no está claro qué implementación debe ejecutarse si llamamos on()a un MysteriousDeviceobjeto. El objeto no entenderá. Y para colmo, NuclearBomb no tiene off()método, por lo que si no acertamos, será imposible apagar el dispositivo. Ejemplos concretos de clases abstractas en Java - 2Este "malentendido", cuando no está claro qué comportamiento se debe ejecutar, es precisamente la razón por la cual los creadores de Java rechazaron la herencia múltiple. Dicho esto, aprenderá que las clases de Java pueden implementar muchas interfaces.