CIAO! Parliamo di classi astratte in Java.Esempi concreti di classi astratte in Java - 1

Perché le classi sono chiamate "astratte"?

Probabilmente ti ricordi cos'è l'astrazione - ne abbiamo discusso prima :) Se te ne sei dimenticato, non preoccuparti. Ricorda, è un principio OOP che dice, durante la progettazione di classi e la creazione di oggetti, dovresti rappresentare solo le proprietà principali dell'entità e scartare quelle secondarie. Ad esempio, se stiamo progettando una SchoolTeacherclasse, l'altezza probabilmente non sarà una proprietà necessaria di un insegnante. In effetti, questa caratteristica non è importante per un insegnante. Ma se stiamo creando una BasketballPlayerclasse, l' altezza sarà una delle caratteristiche più importanti. Bene, una classe astrattaè il "pezzo grezzo" più astratto per un gruppo di classi future. Il pezzo in lavorazione non può essere utilizzato direttamente: è troppo "ruvido". Ma definisce alcuni stati e comportamenti caratteristici che avranno le classi future - i discendenti della classe astratta.

Esempi di classi astratte in Java

Considera un semplice esempio con le auto:

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;
   }
}
Ecco come appare la classe astratta più semplice. Come puoi vedere, niente di speciale :) Perché potremmo averne bisogno? Innanzitutto, fornisce la descrizione più astratta dell'entità di cui abbiamo bisogno: un'auto. La parola chiave astratta significa qualcosa qui. Nel mondo reale non esiste "solo un'auto". Ci sono camion, auto da corsa, berline, coupé e SUV. La nostra classe astratta è semplicemente un "progetto" che utilizzeremo in seguito per creare classi di auto specifiche.

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!");
   }
  
}
Per molti versi, questo è simile a quello di cui abbiamo parlato nelle lezioni sull'ereditarietà. Solo in quel caso avevamo una Carclasse i cui metodi non erano astratti. Ma una tale soluzione presenta diversi svantaggi che vengono risolti nelle classi astratte. Innanzitutto, non è possibile creare un'istanza di una classe astratta:

public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // Error! The Car class is abstract!
   }
}
Il creatore di Java ha creato questa "caratteristica" apposta. Ancora una volta, ricorda: una classe astratta è solo un progetto per future classi "normali" . Non hai bisogno di copie di un progetto, giusto? Allo stesso modo, non è necessario creare istanze di una classe astratta :) E se la Carclasse non fosse astratta, potremmo facilmente crearne istanze:

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.
   }
}
Al momento, il nostro programma ha una specie di macchina incomprensibile. Non è un camion, non un'auto da corsa e non una berlina, ma non è chiaro cosa sia. È il "solo un'auto" che non esiste nella realtà. Lo stesso esempio può essere dato con gli animali. Immagina se il tuo programma avesse Animaloggetti (" solo animali "). Non è chiaro di che tipo sia, a quale famiglia appartenga o quali caratteristiche abbia. Sarebbe strano vederne uno in un programma. Non ci sono "solo animali" in natura. Solo cani, gatti, volpi, talpe, ecc. Le classi astratte ci salvano dai " solo oggetti ". Ci danno lo stato e il comportamento di base. Ad esempio, tutte le auto devono avere un modello , un colore e una velocità massimae devono anche essere in grado di applicare l' acceleratore e il freno . Questo è tutto. È un progetto astratto generale che utilizzerai in seguito per progettare le classi di cui hai bisogno. Nota: anche i due metodi nella classe abstract sono abstract , il che significa che non hanno alcuna implementazione. Il motivo è lo stesso: le classi astratte non creano "comportamenti predefiniti" per "solo automobili". Indicano solo ciò che ogni macchina deve essere in grado di fare. Detto questo, se hai bisogno di un comportamento predefinito, puoi implementare metodi in una classe astratta. Java non lo vieta:

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();
   }
}
Uscita console:
Accelerating!
Come puoi vedere, abbiamo implementato un metodo nella classe astratta, ma non l'altro. Di conseguenza, il comportamento della nostra Sedanclasse è diviso in due parti: se chiamiamo il suo gas()metodo, il comportamento viene "estratto" dalla Carclasse genitore astratta e implementiamo il brake()metodo nella Sedanclasse. È super conveniente e flessibile. Ma la nostra classe non è così astratta ora, vero ? Dopotutto, ha effettivamente implementato metà dei metodi. Il fatto è - e questa è una caratteristica molto importante - una classe è astratta se anche uno solo dei suoi metodi è astratto. Un metodo su due o uno su mille: non importa. Potremmo persino implementare tutti i metodi, senza lasciare nessuno astratto. Il risultato sarebbe una classe astratta senza metodi astratti. Questo è possibile in linea di principio — il compilatore non genererà alcun errore — ma è meglio non farlo, poiché priva la parola abstract del suo significato. Anche i tuoi colleghi programmatori saranno molto sorpresi di vedere questo :/ Detto questo, se un metodo è contrassegnato come astratto, ogni classe discendente deve implementarlo o essere dichiarata come astratta. In caso contrario, il compilatore genererà un errore. Naturalmente, ogni classe può ereditare solo una classe astratta, quindi non c'è differenza tra classi astratte e regolari in termini di ereditarietà. Non importa se ereditiamo una classe astratta o normale: può esserci solo una classe genitore.

Perché Java non ha ereditarietà di più classi

Abbiamo già detto che non esiste un'ereditarietà multipla in Java, ma non abbiamo approfondito il motivo. Proviamo a farlo ora. Il fatto è che se Java avesse ereditarietà multipla, le classi figlie non sarebbero in grado di decidere quale comportamento scegliere. Diciamo che abbiamo due classi: Toastere 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!");
   }
}
Come puoi vedere, entrambe le classi hanno un on()metodo. Per il tostapane, il metodo inizia a fare toast, ma nel caso della bomba nucleare, fa esplodere. Uh-oh :/ Ora immagina di aver deciso (non chiedermi perché!) di creare una via di mezzo. Ecco la tua classe: MysteriousDevice! Questo codice non funzionerà, ovviamente. Lo presentiamo semplicemente come un esempio di "cosa avrebbe potuto essere":

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?
   }
}
Vediamo cosa abbiamo. Il misterioso dispositivo deriva sia da Toaster che da NuclearBomb allo stesso tempo. Entrambi hanno un on()metodo. Di conseguenza, non è chiaro quale implementazione dovrebbe essere eseguita se chiamiamo on()un MysteriousDeviceoggetto. L'oggetto non capirà. E per finire, NuclearBomb non ha un off()metodo, quindi se non indoviniamo correttamente, sarà impossibile spegnere il dispositivo. Esempi concreti di classi astratte in Java - 2Questo "malinteso", quando non è chiaro quale comportamento dovrebbe essere eseguito, è precisamente il motivo per cui i creatori di Java hanno rifiutato l'ereditarietà multipla. Detto questo, imparerai che le classi Java possono implementare molte interfacce.