Dziedziczenie i interfejsy w Javie w prosty sposób

Zaktualizowano 22 października 2026

Dziedziczenie i interfejsy w Javie opierają się na klasach i obiektach i oszczędzają ci pisania tego samego kodu dwa razy. Apartament to pokój hotelowy, tylko droższy. Zamiast pisać wszystko od nowa, dziedziczy po pokoju i zmienia tylko to, co jest inne. Interfejsy idą o krok dalej: określają tylko, co klasa musi umieć, a nie jak. Jedno i drugie pozwala pisać kod, który tak samo obsługuje wiele różnych klas.

Dziedziczenie przez extends

class Suite extends Room oznacza: apartament ma wszystkie pola i metody klasy Room. Room to nadklasa, a Suite to podklasa.

  • Konstruktor podklasy w pierwszej linii wywołuje konstruktor nadklasy przez super(...).
  • Metodę nadklasy możesz nadpisać: ta sama nazwa, te same parametry, nowe ciało. @Override nad nią sprawia, że kompilator sprawdza, czy taka metoda naprawdę istnieje w nadklasie.
  • super.price() wywołuje wersję z nadklasy. W ten sposób ją rozszerzasz, zamiast kopiować wzór.
  • Podklasa nie widzi bezpośrednio pól private nadklasy. Korzysta z getterów, takich jak getNights().

Zmienna typu Room może też przechowywać obiekt Suite. O tym, która metoda price() się wykona, decyduje obiekt, a nie typ zmiennej. Dlatego describe() liczy dla apartamentu wyższą cenę, chociaż describe() jest zapisane tylko w Room. Nazywa się to polimorfizmem.

class Room {    private final int nights;
    public Room(int nights) {        this.nights = nights;    }
    public int getNights() {        return nights;    }
    public double price() {        return 80.0 * nights;    }
    public String describe() {        return nights + " nights: " + price() + " €";    }}
class Suite extends Room {    public Suite(int nights) {        super(nights);    }
    @Override    public double price() {        return super.price() + 50.0 * getNights();    }}
public class Main {    public static void main(String[] args) {        Room room = new Room(2);        Room suite = new Suite(2);        System.out.println(room.describe());        System.out.println(suite.describe());    }}

Klasy abstrakcyjne i interfejsy

Klasa abstrakcyjna (abstract class Vehicle) jest pomyślana tylko jako nadklasa. Nie możesz utworzyć obiektu bezpośrednio z niej. Metoda abstrakcyjna nie ma ciała, tylko nagłówek ze średnikiem. Każda podklasa musi ją zaimplementować, inaczej się nie skompiluje.

Interfejs zawiera tylko nagłówki metod: to umowa. Klasa spełnia ją przez implements i pisze wszystkie metody. Klasa może dziedziczyć tylko po jednej nadklasie, ale implementować dowolnie wiele interfejsów: class Lamp implements Switchable, Dimmable.

Metody i listy (zobacz listy i mapy w Javie), które znają tylko ogólny typ (List<Vehicle>, PaymentMethod method), działają z każdą podklasą. Czy obiekt dodatkowo implementuje interfejs, sprawdza instanceof: if (v instanceof Electric e) od razu daje ci obiekt jako e.

import java.util.ArrayList;import java.util.List;
abstract class Vehicle {    private final String name;
    public Vehicle(String name) {        this.name = name;    }
    public abstract int wheels();
    public String describe() {        return name + " has " + wheels() + " wheels";    }}
interface Electric {    int range();}
class Bicycle extends Vehicle {    public Bicycle() {        super("Bicycle");    }
    @Override    public int wheels() {        return 2;    }}
class ElectricCar extends Vehicle implements Electric {    public ElectricCar() {        super("Electric car");    }
    @Override    public int wheels() {        return 4;    }
    @Override    public int range() {        return 400;    }}
public class Main {    public static void main(String[] args) {        List<Vehicle> fleet = new ArrayList<>();        fleet.add(new Bicycle());        fleet.add(new ElectricCar());        for (Vehicle v : fleet) {            System.out.println(v.describe());            if (v instanceof Electric e) {                System.out.println("  range: " + e.range() + " km");            }        }    }}

Sortowanie z Comparable

Collections.sort(list) sortuje listę, jeśli jej elementy są Comparable. Liczby i teksty już takie są, a własne klasy implementują w tym celu Comparable<Runner>. Jedyna metoda tego interfejsu, compareTo(other), zwraca:

  • liczbę ujemną, jeśli this jest przed other,
  • liczbę dodatnią, jeśli jest po nim,
  • 0, jeśli oba są równe.

Integer.compare(a, b) zwraca dokładnie to dla dwóch liczb. Dla kolejności malejącej zamieniasz argumenty miejscami. Przy remisie porównujesz potem drugą cechę, na przykład imię przez name.compareTo(other.name). Jak algorytm sortowania działa od środka, wyjaśnia artykuł o szybkich algorytmach sortowania.

import java.util.ArrayList;import java.util.Collections;import java.util.List;
class Runner implements Comparable<Runner> {    private final String name;    private final int seconds;
    public Runner(String name, int seconds) {        this.name = name;        this.seconds = seconds;    }
    @Override    public int compareTo(Runner other) {        return Integer.compare(seconds, other.seconds);    }
    @Override    public String toString() {        return name + " (" + seconds + " s)";    }}
public class Main {    public static void main(String[] args) {        List<Runner> runners = new ArrayList<>();        runners.add(new Runner("Mia", 52));        runners.add(new Runner("Leo", 48));        runners.add(new Runner("Ana", 55));        Collections.sort(runners);        System.out.println(runners);
        System.out.println("Ada".compareTo("Bob"));        System.out.println("Bob".compareTo("Ada"));        System.out.println("Ada".compareTo("Ada"));    }}

Typowe błędy

  • Brak super(...)

    Jeśli nadklasa ma tylko konstruktor z parametrami, każda podklasa musi go wywołać przez super(...), i to w pierwszej linii. Inaczej kompilator zgłasza constructor Room in class Room cannot be applied to given types.

  • Nadpisywanie bez @Override

    Literówka w rodzaju public double prize() albo inny typ parametru po cichu tworzy nową metodę, która nigdy nie zostanie wywołana. Z @Override kompilator od razu zgłosi błąd.

  • Ponowne deklarowanie pola w podklasie

    Jeśli podklasa deklaruje pole o tej samej nazwie co nadklasa, obiekt ma potem dwa takie pola. Metody nadklasy widzą tylko swoje:

    class Suite extends Room {    private int nights; // hides Room's nights, stays 0}

    Pola należą tylko do klasy, która je wprowadza.

  • Obiekt klasy abstrakcyjnej

    Kompilator odrzuca new Vehicle("Bus") z komunikatem Vehicle is abstract; cannot be instantiated. Utwórz obiekt podklasy; możesz go spokojnie zapisać w zmiennej typu Vehicle.

Zadania z tego tematu

Przećwicz to, czego się nauczyłeś, od razu w przeglądarce. Testy natychmiast sprawdzają twoje rozwiązanie.

Powiązane artykuły