0Pricing
Java Academy · Lekcja

Zasady przeciążania metod

Poznaj sposób rozstrzygania przeciążonych metod przez Javę oraz typowe pułapki związane z niejednoznacznością.

Zasady przeciążania metod to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Zasady przeciążania metod

Przeciążanie metod pozwala definiować wiele metod o tej samej nazwie, ale różnych listach parametrów. Java wybiera najlepsze dopasowanie podczas kompilacji, stosując precyzyjne zasady rozstrzygania.

Czym jest przeciążanie

Przeciążone metody mają wspólną nazwę, ale różnią się liczbą, typem lub kolejnością parametrów. Sam typ zwracany NIE wystarcza do przeciążenia metody.

class Calculator {
    int add(int a, int b)     { return a + b; }
    double add(double a, double b) { return a + b; }
    int add(int a, int b, int c)   { return a + b + c; }

    // compile error — same signature, different return type only:
    // long add(int a, int b) { return a + b; }
}

Calculator calc = new Calculator();
System.out.println(calc.add(1, 2));       // 3 (int)
System.out.println(calc.add(1.5, 2.5));   // 4.0 (double)
System.out.println(calc.add(1, 2, 3));    // 6 (3-arg)

Rozstrzyganie: najbardziej szczegółowe dopasowanie

Java wybiera najbardziej szczegółową pasującą metodę. Rozszerzanie typów, autoboxing i varargs są rozpatrywane w tej kolejności preferencji.

static void print(int x)    { System.out.println("int: "    + x); }
static void print(long x)   { System.out.println("long: "   + x); }
static void print(Object x) { System.out.println("Object: " + x); }

print(42);       // int: 42 (exact match — most specific)
print(42L);      // long: 42 (exact match)
print(3.14);     // Object: 3.14 (no float match, widening then autoboxing)

Rozszerzanie typów przed autoboxingiem

Java preferuje rozszerzenie typu prymitywnego zamiast opakowania go w typ opakowujący za pomocą autoboxingu. Może to prowadzić do zaskakujących wyników.

static void display(long n)    { System.out.println("long: " + n); }
static void display(Integer n) { System.out.println("Integer: " + n); }

int x = 100;
display(x); // long: 100 — widens int to long BEFORE autoboxing to Integer
// This surprises many developers!

static void show(Long n)    { System.out.println("Long"); }
static void show(Integer n) { System.out.println("Integer"); }

// show(100); // compile error — ambiguous! Neither is more specific

Niejednoznaczność argumentu null

Przekazanie null do przeciążonych metod może być niejednoznaczne, jeśli wiele metod przyjmuje typy referencyjne. Aby rozstrzygnąć niejednoznaczność, należy rzutować null.

static void handle(String s)  { System.out.println("String: " + s); }
static void handle(Integer i) { System.out.println("Integer: " + i); }
static void handle(Object o)  { System.out.println("Object"); }

handle((String) null);  // String: null (cast resolves ambiguity)
handle((Integer) null); // Integer: null
// handle(null);        // compile error: ambiguous (String vs Integer)

Przeciążanie a dziedziczenie

Podczas rozstrzygania Java bierze pod uwagę zadeklarowany typ zmiennej referencyjnej, a nie typ czasu wykonania — jest to decyzja podejmowana podczas kompilacji.

class Animal {}
class Dog extends Animal {}

static void greet(Animal a) { System.out.println("Animal"); }
static void greet(Dog d)    { System.out.println("Dog"); }

Animal a = new Dog(); // declared as Animal
greet(a);   // Animal — compile-time type is Animal!

Dog d = new Dog();
greet(d);   // Dog — compile-time type is Dog

Przeciążanie a przesłanianie

Przeciążanie jest decyzją podejmowaną podczas kompilacji (wiązanie statyczne). Przesłanianie jest decyzją podejmowaną podczas działania programu (wiązanie dynamiczne), na podstawie rzeczywistego typu obiektu.

class Base {
    void run() { System.out.println("Base.run"); }
}
class Sub extends Base {
    @Override
    void run() { System.out.println("Sub.run"); } // overriding

    void run(int speed) { System.out.println("Sub.run(" + speed + ")"); } // overloading
}

Base b = new Sub();
b.run();     // Sub.run — override (runtime dispatch)
// b.run(10); // compile error — Base doesn't have run(int)

Typowe wzorce przeciążania

Typowe zastosowanie przeciążania w API: udostępnianie metod pomocniczych, które delegują wykonanie do wersji przyjmującej pełny zestaw parametrów.

class Connection {
    void connect(String host, int port, int timeout) {
        System.out.println("Connecting to " + host + ":" + port + " timeout=" + timeout);
    }

    // Convenience overloads
    void connect(String host, int port) {
        connect(host, port, 30); // delegate with default timeout
    }

    void connect(String host) {
        connect(host, 5432); // delegate with default port
    }
}

new Connection().connect("db.example.com");
// Connecting to db.example.com:5432 timeout=30

Przeciążanie a typy generyczne

Przeciążanie metod generycznych wymaga ostrożności — usuwanie informacji o typach może sprawić, że dwie pozornie różne sygnatury będą identyczne po kompilacji.

// Type erasure problem: both methods erase to the same signature!
// static void process(List<String> list) {} // compile error
// static void process(List<Integer> list) {} // duplicate after erasure

// Solutions:
// 1. Use different method names: processStrings / processIntegers
// 2. Use a generic method with bounded type:
static <T extends Number> void processNumbers(List<T> list) {
    list.forEach(n -> System.out.println(n.doubleValue()));
}
processNumbers(List.of(1, 2, 3));
processNumbers(List.of(1.5, 2.5));

Praktyczny przykład: przeciążone metody w stylu buildera

Przeciążanie jest często stosowane w interfejsach builderów i fabryk, aby zapewnić elastyczne tworzenie obiektów.

class Notification {
    final String title, body;
    final String imageUrl;
    final boolean urgent;

    // Factory overloads for convenience
    static Notification simple(String title, String body) {
        return new Notification(title, body, null, false);
    }
    static Notification urgent(String title, String body) {
        return new Notification(title, body, null, true);
    }
    static Notification withImage(String title, String body, String url) {
        return new Notification(title, body, url, false);
    }
    Notification(String title, String body, String imageUrl, boolean urgent) {
        this.title = title; this.body = body;
        this.imageUrl = imageUrl; this.urgent = urgent;
    }
}

Pułapki przeciążania

Należy unikać tych typowych błędów związanych z przeciążaniem:

  • Zbyt wielu przeciążonych wersji tej samej operacji — zamiast tego należy użyć buildera
  • Przeciążonych metod różniących się tylko kolejnością parametrów — jest to mylące
  • Zaskakującego rozszerzania typów: wywołanie z int może zostać dopasowane do long zamiast Integer
// Confusing: order-based overloads
// void save(String name, int age)  {}
// void save(int age, String name)  {} // very confusing!

// Better: use a record/DTO
record UserData(String name, int age) {}
void save(UserData data) {}

// Surprising widening
static void m(long x)   { System.out.println("long"); }
static void m(Integer x){ System.out.println("Integer"); }
int v = 5;
m(v); // long! widening preferred over autoboxing

Szybki test

Jeśli metoda jest przeciążona zarówno parametrem long, jak i Integer, który wariant zostanie wybrany po przekazaniu argumentu int?

Podsumowanie: zasady przeciążania metod

Najważniejsze informacje:

  • Przeciążenia różnią się liczbą, typem lub kolejnością parametrów — NIE tylko typem zwracanym
  • Java rozstrzyga przeciążenia podczas kompilacji, korzystając z zadeklarowanych typów argumentów
  • Rozszerzanie typu prymitywnego ma pierwszeństwo przed automatycznym opakowaniem go w typ opakowujący
  • Argumenty null mogą powodować niejednoznaczność — użyj rzutowania, aby ją rozstrzygnąć: handle((String) null)
  • Przeciążanie odbywa się podczas kompilacji, a przesłanianie podczas działania programu — są to różne mechanizmy
  • Używaj wygodnych przeciążeń, które delegują wywołanie do głównej metody przyjmującej wszystkie parametry

Często zadawane pytania

Czy lekcja „Zasady przeciążania metod” jest bezpłatna?

Tak — pełny tekst „Zasady przeciążania metod” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Zasady przeciążania metod”?

Poznaj sposób rozstrzygania przeciążonych metod przez Javę oraz typowe pułapki związane z niejednoznacznością. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Zasady przeciążania metod”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Zasady przeciążania metod
  2. Parametry varargs
  3. Pola statyczne i stałe
  4. Klasy narzędziowe i statyczne metody fabrykujące
← Powrót do Java Academy