CodeGym /Kursy /JAVA 25 SELF /Klasy i metody abstrakcyjne: składnia, przykłady

Klasy i metody abstrakcyjne: składnia, przykłady

JAVA 25 SELF
Poziom 19, Lekcja 1
Dostępny

1. Składnia klasy abstrakcyjnej

Dajmy sobie wyjaśnić, czym jest klasa abstrakcyjna w Javie i po co w ogóle jest potrzebna. Wyobraź sobie, że masz zestaw LEGO: masz zestaw elementów (na przykład „kółka”, „klocki”) oraz specjalne elementy dla konkretnych modeli. Klasa abstrakcyjna to jak ogólna instrukcja obchodzenia się z elementami bez opisu składania konkretnego modelu: klocki łączą się wypustkami, kółka mocuje się do osi, elementy można dokładać warstwami. Klasa abstrakcyjna opisuje właśnie te wspólne zasady i obowiązkowe kroki, ale nie rozpisuje montażu konkretnego modelu. Konkretnymi modelami są podklasy: biorą ogólną instrukcję i dodają brakujące kroki.

W Javie klasa abstrakcyjna to klasa, której nie można utworzyć bezpośrednio (nie da się zrobić new AbstractClass()), ale można po niej dziedziczyć i implementować jej „niedopisane” metody.

Klasy abstrakcyjne są potrzebne, gdy:

  • Grupa obiektów ma wspólne zachowanie lub stan, ale część logiki się różni.
  • Chcesz zrealizować część funkcjonalności „domyślnie”, a część pozostawić do implementacji przez potomków.
  • Chcesz zabronić bezpośredniego tworzenia obiektów tej klasy (na przykład „Zwierzę” samo w sobie nie występuje, ale „Kot” – jak najbardziej).

Jak zadeklarować klasę abstrakcyjną

To proste: przed słowem class piszemy słowo kluczowe abstract.

public abstract class Animal {
    // Pola (np. imię zwierzęcia)
    protected String name;

    // Konstruktor
    public Animal(String name) {
        this.name = name;
    }

    // Metoda abstrakcyjna – tylko deklaracja, bez implementacji!
    public abstract void makeSound();

    // Zwykła (zaimplementowana) metoda
    public void sleep() {
        System.out.println(name + " śpi: Zzz...");
    }
}

Charakterystyka:

  • Klasa abstrakcyjna może zawierać zarówno metody zaimplementowane, jak i abstrakcyjne.
  • Klasa abstrakcyjna może zawierać pola, konstruktory, a nawet metody static.
  • Nie można utworzyć obiektu klasy abstrakcyjnej bezpośrednio:
  • Animal a = new Animal("Ktoś"); // Błąd!

Jak zadeklarować metodę abstrakcyjną

Metoda abstrakcyjna to metoda bez ciała, czyli bez nawiasów klamrowych i kodu w środku. Deklaruje się ją słowem kluczowym abstract i koniecznie kończy średnikiem ;.

public abstract void makeSound();
  • Metody abstrakcyjne można deklarować tylko wewnątrz klasy abstrakcyjnej.
  • Klasa, która dziedziczy po klasie abstrakcyjnej, musi zaimplementować wszystkie metody abstrakcyjne, w przeciwnym razie sama staje się abstract.

2. Dziedziczenie klas abstrakcyjnych: jak to działa

Spójrzmy na konkretny przykład. Niech mamy klasę abstrakcyjną Animal z metodą abstrakcyjną makeSound(). Teraz utworzymy klasę potomną Dog, która implementuje tę metodę:

public class Dog extends Animal {
    public Dog(String name) {
        super(name); // Wywołanie konstruktora klasy bazowej
    }

    @Override
    public void makeSound() {
        System.out.println(name + " szczeka: Hau-hau!");
    }
}

I jeszcze jedna klasa potomna:

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void makeSound() {
        System.out.println(name + " miauczy: Miau!");
    }
}

Teraz możemy użyć tych klas w programie:

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog("Rex");
        Animal cat = new Cat("Kitty");

        dog.makeSound(); // Rex szczeka: Hau-hau!
        cat.makeSound(); // Kitty miauczy: Miau!

        dog.sleep(); // Rex śpi: Zzz...
        cat.sleep(); // Kitty śpi: Zzz...
    }
}

Zwróć uwagę:
Możemy deklarować zmienne typu Animal, ale tworzyć obiekty tylko konkretnych (nieabstrakcyjnych) potomków.

Schemat: jak to wygląda


.         Animal (abstract)
            /         \
         Dog         Cat
 (implementuje makeSound) (implementuje makeSound)

3. Kiedy użyć klasy abstrakcyjnej, a kiedy interfejsu?

To jedno z najczęstszych pytań na rozmowach kwalifikacyjnych – i słusznie! Przyjrzyjmy się temu:

  • Klasa abstrakcyjna – gdy obiekty mają wspólny stan (na przykład pola), wspólną logikę (metody z implementacją) i chcesz dać „szkielet” zachowania z możliwością dopracowania.
  • Interfejs – gdy chcesz zdefiniować tylko zestaw metod (kontrakt), bez implementacji i stanu. Od Java 8 interfejsy otrzymały metody default/static, ale wciąż interfejs dotyczy „tego, co obiekt ma umieć”, a nie „jak to robi”.

Przykład z życia:
„Ptak” – klasa abstrakcyjna: wszystkie ptaki mają dziób, skrzydła i potrafią latać (choć różnie).
„Latający” – interfejs: nie tylko ptaki potrafią latać, ale też samoloty i superbohaterowie! Latają wszyscy inaczej, ale ważne, że to potrafią.

4. Praktyczne przykłady

Przykład 1: Klasa abstrakcyjna z częściową implementacją

Załóżmy, że tworzysz grę, w której są różne rodzaje transportu. Wszystkie potrafią jechać, ale robią to na różne sposoby. Jednak wszystkie mają prędkość, nazwę i standardowy sposób zatrzymywania się.

public abstract class Transport {
    protected String name;
    protected int speed;

    public Transport(String name, int speed) {
        this.name = name;
        this.speed = speed;
    }

    // Metoda abstrakcyjna – implementacja w klasach potomnych
    public abstract void move();

    // Zaimplementowana metoda
    public void stop() {
        System.out.println(name + " zatrzymał się.");
    }
}

public class Car extends Transport {
    public Car(String name, int speed) {
        super(name, speed);
    }

    @Override
    public void move() {
        System.out.println(name + " jedzie po drodze z prędkością " + speed + " km/h.");
    }
}

public class Bicycle extends Transport {
    public Bicycle(String name, int speed) {
        super(name, speed);
    }

    @Override
    public void move() {
        System.out.println(name + " kręci pedałami z prędkością " + speed + " km/h.");
    }
}

Użycie:

Transport car = new Car("Toyota", 120);
Transport bike = new Bicycle("Stels", 25);

car.move(); // Toyota jedzie po drodze z prędkością 120 km/h.
bike.move(); // Stels kręci pedałami z prędkością 25 km/h.

car.stop(); // Toyota zatrzymał się.
bike.stop(); // Stels zatrzymał się.

Przykład 2: Metoda abstrakcyjna z parametrem

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

public class Circle extends Shape {
    private double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

public class Rectangle extends Shape {
    private double width, height;
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    @Override
    public double area() {
        return width * height;
    }
}

Użycie:

Shape c = new Circle(3);
Shape r = new Rectangle(4, 5);

System.out.println("Pole koła: " + c.area());
System.out.println("Pole prostokąta: " + r.area());

5. Cechy i niuanse klas abstrakcyjnych

  • Klasa abstrakcyjna może mieć dowolne modyfikatory dostępu: public, protected, private (na przykład dla pól).
  • Można zadeklarować klasę abstrakcyjną bez metod abstrakcyjnych. Bywa to przydatne, jeśli chcesz jedynie zabronić tworzenia egzemplarzy klasy bazowej.
  • Jeśli klasa dziedziczy po klasie abstrakcyjnej, ale nie implementuje wszystkich metod abstrakcyjnych, również musi być zadeklarowana jako abstract.
  • Klasa abstrakcyjna może mieć konstruktory. Są one wywoływane przy tworzeniu obiektu potomnego (przez super(...)).
  • Metody abstrakcyjne nie mogą być private (w przeciwnym razie nie da się ich zaimplementować w klasie potomnej).
  • Klasy abstrakcyjne mogą zawierać metody i pola static.
  • Klasa abstrakcyjna może implementować interfejsy, ale nie musi implementować ich metod – mogą to zrobić potomkowie.

Tabela: porównanie klasy abstrakcyjnej i interfejsu

Klasa abstrakcyjna Interfejs
Słowo kluczowe
abstract class
interface
Może zawierać pola Tak (dowolne) Od Java 8 – tylko static/final
Metody z implementacją Tak Od Java 8 – default/static
Metody abstrakcyjne Tak Tak
Dziedziczenie wielokrotne Nie Tak (można implementować wiele interfejsów)
Konstruktory Tak Nie
Można utworzyć obiekt Nie Nie

6. Typowe błędy przy pracy z klasami i metodami abstrakcyjnymi

Błąd nr 1: próba utworzenia instancji klasy abstrakcyjnej.
Jeśli napiszesz new Animal("Ktoś"), kompilator od razu przypomni, że klasy abstrakcyjne nie są do tego przeznaczone. Pamiętaj: abstrakcja to „szkielet”, a nie „żywy organizm”.

Błąd nr 2: zapomniano zaimplementować wszystkie metody abstrakcyjne w klasie potomnej.
Jeśli Twoja klasa nie implementuje choć jednej metody abstrakcyjnej klasy bazowej, sama musi być zadeklarowana jako abstract, w przeciwnym razie otrzymasz błąd kompilacji.

Błąd nr 3: zadeklarowano metodę abstrakcyjną poza klasą abstrakcyjną.
W Javie nie można zadeklarować metody abstrakcyjnej w zwykłej (nieabstrakcyjnej) klasie – kompilator od razu zaprotestuje.

Błąd nr 4: próba uczynienia metody abstrakcyjnej private lub static.
Metody abstrakcyjne nie mogą być private (bo nie da się ich nadpisać) ani static (bo metody statyczne się nie nadpisują). No i nie mogą być też final, ponieważ final zabrania nadpisania.

Błąd nr 5: zapomniano o modyfikatorze dostępu przy metodzie abstrakcyjnej.
Jeśli jawnie nie podasz modyfikatora, metoda będzie miała widoczność pakietową (package-private), co nie zawsze jest tym, o co Ci chodziło.

1
Zadanie
JAVA 25 SELF, poziom 19, lekcja 1
Niedostępne
Kolejny mieszkaniec cyfrowego zoo 🐾
Kolejny mieszkaniec cyfrowego zoo 🐾
1
Zadanie
JAVA 25 SELF, poziom 19, lekcja 1
Niedostępne
System sterowania "Inteligentnym domem" 📱
System sterowania "Inteligentnym domem" 📱
1
Zadanie
JAVA 25 SELF, poziom 19, lekcja 1
Niedostępne
Edytor geometryczny: figury i ich pola powierzchni 📐
Edytor geometryczny: figury i ich pola powierzchni 📐
1
Zadanie
JAVA 25 SELF, poziom 19, lekcja 1
Niedostępne
System kadrowy firmy IT 💼
System kadrowy firmy IT 💼
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION