Bună! Să vorbim despre clasele abstracte în Java.Exemple concrete de clase abstracte în Java - 1

De ce sunt clasele numite „abstracte”?

Probabil vă amintiți ce este abstracția — am discutat mai devreme :) Dacă ați uitat, nu vă faceți griji. Amintiți-vă, este un principiu POO care spune că, atunci când proiectați clase și creați obiecte, ar trebui să reprezentați numai proprietățile principale ale entității și să le eliminați pe cele secundare. De exemplu, dacă proiectăm o SchoolTeacherclasă, probabil că înălțimea nu va fi o proprietate necesară a unui profesor. Într-adevăr, această caracteristică nu este importantă pentru un profesor. Dar dacă creăm o BasketballPlayerclasă, atunci înălțimea va fi una dintre cele mai importante caracteristici. Ei bine, o clasă abstractăeste cea mai abstractă „piesa de lucru” pentru un grup de clase viitoare. Piesa de prelucrat nu poate fi utilizată direct - este prea „aspră”. Dar definește o anumită stare și comportament caracteristic pe care clasele viitoare - descendenții clasei abstracte - le vor avea.

Exemple de clase abstracte în Java

Luați în considerare un exemplu simplu cu mașini:

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;
   }
}
Așa arată cea mai simplă clasă abstractă. După cum puteți vedea, nimic special :) De ce am putea avea nevoie de asta? În primul rând, oferă cea mai abstractă descriere a entității de care avem nevoie - o mașină. Cuvântul cheie abstract înseamnă ceva aici. În lumea reală, nu există „doar o mașină”. Există camioane, mașini de curse, sedanuri, coupe-uri și SUV-uri. Clasa noastră abstractă este pur și simplu un „plan” pe care îl vom folosi ulterior pentru a crea clase de mașini specifice.

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!");
   }
  
}
În multe privințe, acest lucru este similar cu ceea ce am vorbit în lecțiile despre moștenire. Doar în acest caz aveam o Carclasă ale cărei metode nu erau abstracte. Dar o astfel de soluție are câteva dezavantaje care sunt fixate în clasele abstracte. În primul rând, o instanță a unei clase abstracte nu poate fi creată:

public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // Error! The Car class is abstract!
   }
}
Creatorul Java a creat această „funcție” intenționat. Încă o dată, amintiți-vă: o clasă abstractă este doar un model pentru viitoarele clase „obișnuite” . Nu aveți nevoie de copii ale unui plan, nu? În mod similar, nu este nevoie să creați instanțe ale unei clase abstracte :) Și dacă Carclasa nu ar fi abstractă, atunci am putea crea cu ușurință instanțe ale acesteia:

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.
   }
}
În prezent, programul nostru are un fel de mașină de neînțeles. Nu este un camion, nu este o mașină de curse și nu este un sedan, dar nu este chiar clar ce este. Este „doar o mașină” care nu există în realitate. Același exemplu poate fi dat și cu animalele. Imaginează-ți dacă programul tău ar avea Animalobiecte (" doar animale "). Nu este clar ce fel este, din ce familie aparține sau ce caracteristici are. Ar fi ciudat să vezi unul într-un program. Nu există „doar animale” în natură. Doar câini, pisici, vulpi, alunițe etc. Clasele abstracte ne salvează de „ doar obiecte ”. Ele ne oferă starea de bază și comportamentul. De exemplu, toate mașinile trebuie să aibă model , culoare și viteză maximă, și trebuie să poată de asemenea să aplice gazul și frâna . Asta este. Este un plan general abstract pe care îl veți folosi mai târziu pentru a proiecta clasele de care aveți nevoie. Notă: cele două metode din clasa abstractă sunt, de asemenea, abstracte , ceea ce înseamnă că nu au deloc implementare. Motivul este același: clasele abstracte nu creează „comportamente implicite” pentru „doar mașini”. Ele indică doar ce trebuie să poată face fiecare mașină. Acestea fiind spuse, dacă aveți nevoie de comportament implicit, puteți implementa metode într-o clasă abstractă. Java nu interzice acest lucru:

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();
   }
}
Ieșire din consolă:
Accelerating!
După cum puteți vedea, am implementat o metodă în clasa abstractă, dar nu și cealaltă. Ca urmare, comportamentul Sedanclasei noastre este împărțit în două părți: dacă numim gas()metoda sa, comportamentul este „tras în sus” din Carclasa părinte abstractă și implementăm brake()metoda în Sedanclasă. Este foarte convenabil și flexibil. Dar clasa noastră nu este atât de abstractă acum, nu-i așa ? La urma urmei, a implementat de fapt jumătate din metode. Faptul este - și aceasta este o caracteristică foarte importantă - o clasă este abstractă dacă chiar și una dintre metodele sale este abstractă. O metodă din două sau una din o mie - nu contează. Am putea chiar să implementăm toate metodele, fără a lăsa niciuna abstractă. Rezultatul ar fi o clasă abstractă fără metode abstracte. Acest lucru este posibil în principiu - compilatorul nu va genera erori - dar este mai bine să nu faceți acest lucru, deoarece privează cuvântul abstract de sensul său. Colegii tăi programatori vor fi, de asemenea, foarte surprinși să vadă acest lucru :/ Acestea fiind spuse, dacă o metodă este marcată ca abstractă, fiecare clasă descendentă trebuie să o implementeze sau să fie declarată abstractă. În caz contrar, compilatorul va arunca o eroare. Desigur, fiecare clasă poate moșteni doar o clasă abstractă, deci nu există nicio diferență între clasele abstracte și obișnuite în ceea ce privește moștenirea. Nu contează dacă moștenim o clasă abstractă sau una obișnuită - poate exista o singură clasă părinte.

De ce Java nu are moștenire de mai multe clase

Am spus deja că nu există o moștenire multiplă în Java, dar nu am aprofundat cu adevărat de ce. Să încercăm să facem asta acum. Faptul este că, dacă Java ar avea moștenire multiplă, atunci clasele copil nu ar putea decide ce comportament să aleagă. Să presupunem că avem două clase: Toasterși 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!");
   }
}
După cum puteți vedea, ambele clase au o on()metodă. Pentru prăjitor de pâine, metoda începe să facă pâine prăjită, dar în cazul bombei nucleare, declanșează o explozie. Uh-oh :/ Acum imaginați-vă că ați decis (nu mă întrebați de ce!) să creați ceva între ele. Iată clasa ta: MysteriousDevice! Acest cod nu va funcționa, desigur. Îl prezentăm simplu ca un exemplu de „ce ar fi putut fi”:

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?
   }
}
Să vedem ce avem. Dispozitivul misterios derivă atât de la Toaster, cât și de la NuclearBomb în același timp. Ambele au o on()metodă. Ca urmare, nu este clar care implementare ar trebui să fie executată dacă apelăm on()la un MysteriousDeviceobiect. Obiectul nu va înțelege. Și, culmea, NuclearBomb nu are o off()metodă, așa că dacă nu ghicim corect, atunci va fi imposibil să oprim dispozitivul. Exemple concrete de clase abstracte în Java - 2Această „neînțelegere”, când nu este clar ce comportament trebuie executat, este tocmai motivul pentru care creatorii Java au respins moștenirea multiplă. Acestea fiind spuse, veți învăța că clasele Java pot implementa multe interfețe.