Cześć! Porozmawiajmy o klasach abstrakcyjnych w Javie. Klasy abstrakcyjne w Javie z konkretnymi przykładami - 1

Dlaczego klasy nazywane są „abstrakcyjnymi”?

Pewnie pamiętasz, czym jest „abstrakcja” – już to przerabialiśmy :) Jeśli nagle zapomniałeś – nie szkodzi, pamiętaj: taka jest zasada OOP , zgodnie z którą przy projektowaniu klas i tworzeniu obiektów należy podkreślić tylko główne właściwości jednostki i odrzucić drugorzędne. Na przykład, jeśli projektujemy klasę SchoolTeacher- nauczyciela szkolnego - jest mało prawdopodobne, że będziemy potrzebować cechy „ wzrostu ”. Rzeczywiście: dla nauczyciela ta cecha nie jest ważna. Ale jeśli stworzymy w programie klasę BasketballPlayer- koszykarza - wzrost stanie się jedną z głównych cech. A więc klasa abstrakcyjna- to jest najbardziej abstrakcyjne, ach-o-o-tak przybliżone "puste" dla grupy przyszłych zajęć. Ten blank nie może być używany w gotowej formie - zbyt "surowy". Ale opisuje pewien ogólny stan i zachowanie, jakie będą miały przyszłe klasy - spadkobiercy klasy abstrakcyjnej.

Przykłady klas abstrakcyjnych w Javie

Rozważ prosty przykład samochodu:

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;
   }
}
Tak wygląda najprostsza klasa abstrakcyjna. Jak widać, nic specjalnego :) Do czego może nam się przydać? Przede wszystkim opisuje w sposób możliwie najbardziej abstrakcyjny byt, którego potrzebujemy - samochód. Słowo abstrakcja nie jest tu na próżno. Na świecie nie ma „tylko samochodów”. Są ciężarówki, samochody wyścigowe, sedany, coupe, SUV-y. Nasza klasa abstrakcyjna to tylko „plan”, z którego później stworzymy klasy samochodów.

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!");
   }
  
}
Jest to pod wieloma względami podobne do tego, o czym mówiliśmy na wykładach o dziedziczeniu. Tylko tam mamy klasę Cari jej metody nie były abstrakcyjne. Ale takie rozwiązanie ma szereg wad, które są naprawiane w klasach abstrakcyjnych. Przede wszystkim nie można utworzyć instancji klasy abstrakcyjnej:

public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // Error! The Car class is abstract!
   }
}
Ten „chip” został celowo zaimplementowany przez twórców Javy. Jeszcze raz, dla przypomnienia: klasa abstrakcyjna to tylko plan przyszłych „normalnych” klas . Nie potrzebujesz kopii planów, prawda? Nie trzeba więc tworzyć instancji klasy abstrakcyjnej :) A gdyby klasa Carnie była abstrakcyjna, moglibyśmy łatwo stworzyć jej obiekty:

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.
   }
}
Teraz mamy w naszym programie jakiś niezrozumiały samochód - nie ciężarówkę, nie samochód wyścigowy, nie sedan, ale ogólnie nie jest jasne, co. Ta sama „tylko maszyna”, która nie istnieje w naturze. Ten sam przykład można podać ze zwierzętami. Wyobraź sobie, że w twoim programie pojawiły się obiekty Animal- „ tylko zwierzę ”. Nie jest jasne, jaki to rodzaj, do jakiej rodziny należy, jakie ma cechy. Dziwnie byłoby go zobaczyć w programie. W przyrodzie nie ma „tylko zwierząt”. Tylko psy, koty, lisy, krety i inne. Klasy abstrakcyjne pozbywają się „ tylko obiektów ”. Dają nam stan podstawowy i zachowanie. Na przykład wszystkie samochody muszą mieć model , kolor i maksymalną prędkość., a także muszą być w stanie przyspieszać i hamować . To wszystko. To jest ogólny schemat abstrakcyjny, wtedy sam projektujesz potrzebne klasy. Zauważ, że dwie metody w klasie abstract są również oznaczone jako abstract i nie są w ogóle zaimplementowane. Powód jest ten sam: klasy abstrakcyjne nie tworzą „domyślnych zachowań” dla „tylko maszyn”. Mówią tylko, że powinni umieć obsługiwać wszystkie maszyny. Jeśli jednak nadal potrzebujesz zachowania domyślnego, można zaimplementować metody w klasie abstrakcyjnej. Java tego nie zabrania:

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();
   }
}
Wyjście konsoli:
Accelerating!
Jak widać, zaimplementowaliśmy jedną metodę w klasie abstrakcyjnej, ale nie zaimplementowaliśmy drugiej. W rezultacie zachowanie naszej klasy Sedanzostało podzielone na dwie części: jeśli wywołasz jej metodę gas(), zostanie ona „wyciągnięta” z nadrzędnej klasy abstrakcyjnej Car, a brake()my nadpiszemy metodę w klasie Sedan. Okazało się to bardzo wygodne i elastyczne. Ale teraz nasza klasa wcale nie jest taka abstrakcyjna ? W końcu w rzeczywistości wdrożył połowę metod. W rzeczywistości – i to jest bardzo ważna cecha – klasa jest abstrakcyjna, jeśli przynajmniej jedna z jej metod jest abstrakcyjna.. Przynajmniej jedna z dwóch, przynajmniej jedna z tysiąca metod - to nie ma znaczenia. Możemy nawet zaimplementować wszystkie metody i nie pozostawić żadnej z abstrakcyjnych. Będzie klasa abstrakcyjna bez metod abstrakcyjnych. W zasadzie jest to możliwe, a kompilator nie będzie generował błędów, ale lepiej tego nie robić: słowo abstract straci swoje znaczenie, a twoi koledzy programiści będą bardzo zdziwieni, widząc to :/ Co więcej, jeśli metoda jest oznaczona słowem abstract, każda klasa, która ją dziedziczy, musi implementować lub zostać zadeklarowana jako abstrakcyjna. W przeciwnym razie kompilator zgłosi błąd . Oczywiście każda klasa może dziedziczyć tylko po jednej klasie abstrakcyjnej, więc nie ma różnicy między klasami abstrakcyjnymi i zwykłymi pod względem dziedziczenia. Nie ma znaczenia, czy dziedziczymy po klasie abstrakcyjnej, czy zwykłej, klasa nadrzędna może być tylko jedna.

Dlaczego Java nie ma dziedziczenia wielu klas

Powiedzieliśmy już, że w Javie nie ma wielokrotnego dziedziczenia, ale tak naprawdę nie rozumieliśmy, dlaczego. Spróbujmy zrobić to teraz. Faktem jest, że gdyby Java miała dziedziczenie wielokrotne, klasy potomne nie mogłyby zdecydować, które zachowanie wybrać. Powiedzmy, że mamy dwie klasy - Toasteri 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!");
   }
}
Jak widać, oba mają on(). W przypadku tostera zaczyna robić tosty, aw przypadku bomby atomowej wywołuje eksplozję. Oh :/ A teraz wyobraź sobie, że postanowiłeś (nie wiem dlaczego tak nagle!) stworzyć coś pomiędzy. A oto twoja klasa - MysteriousDevice! Ten kod oczywiście nie działa i podajemy go po prostu jako przykład „co to może być”:

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?
   }
}
Zobaczmy, co mamy. Tajemnicze urządzenie pochodzi jednocześnie z Tostera i Bomby Nuklearnej. Obie mają metodę on(), w wyniku czego nie jest jasne, która z metod on()powinna działać na obiekcie, MysteriousDevicejeśli go nazwiemy. Obiekt nie będzie w stanie tego zrozumieć. Cóż, jako wisienka na torcie: bomba atomowa nie ma metody off(), więc jeśli się nie domyśliliśmy, wyłączenie urządzenia będzie niemożliwe. Klasy abstrakcyjne w Javie z konkretnymi przykładami - 2 To właśnie z powodu tego „nieporozumienia”, gdy obiekt nie jest jasny, jakie zachowanie powinien wybrać, twórcy Javy zrezygnowali z dziedziczenia wielokrotnego. Dowiesz się jednak, że klasy Javy implementują wiele interfejsów.