PrzedsprzedażAkademia AI w testach i jakości oprogramowania: 1 350 zł zamiast 1 750 zł netto do 30 listopada

Startuje przedsprzedaż Akademii AI w testach i jakości oprogramowania: 5 tygodni z Tomaszem Stelmachem, start 11 stycznia 2027. Wyjątkowa cena 1 350 zł netto zamiast 1 750 zł tylko do 30 listopada.

zostało 25 dniZobaczcie program i zapiszcie się
11. Instrukcja switch w Javie: klasyczna składnia, strzałka i switch expression
Czas czytania: 14 minut

Instrukcja switch w Javie wybiera jedną z wielu gałęzi kodu na podstawie wartości jednego wyrażenia: liczby, znaku, tekstu albo stałej typu wyliczeniowego. Zastępuje długie łańcuchy if i else if, a od Javy 14 może też zwracać wartość jako switch expression. W tej lekcji kursu Java dla testerów pokazujemy wszystkie trzy formy, pułapkę braku break i przykłady wprost z frameworka testów automatycznych.

Jeśli dopiero zaczynacie, warto mieć za sobą lekcję 9 o instrukcji warunkowej if, bo switch rozwiązuje ten sam problem, tylko w innym kształcie. Każdy fragment kodu z tego artykułu skompilowaliśmy poleceniem javac --release 25 i uruchomiliśmy, a wynik działania jest w komentarzach obok.

Lekcja 11 kursu Java w wersji wideo. Artykuł rozszerza ją o składnię ze strzałką, switch expression i wzorce z Javy 21.

Czym jest instrukcja switch w Javie i kiedy zastępuje if?

Instrukcja switch to instrukcja warunkowa, która porównuje wartość jednego wyrażenia, zwanego selektorem, z listą stałych w etykietach case i wykonuje kod pasującej gałęzi. Gdy żadna etykieta nie pasuje, sterowanie trafia do default, o ile go napisaliście. Tam, gdzie if sprawdza dowolne warunki logiczne, switch odpowiada na jedno pytanie: która z wartości to jest.

Z tego wynika prosta reguła wyboru. Gdy porównujecie jedną zmienną z kilkoma konkretnymi wartościami, na przykład nazwą przeglądarki, środowiskiem albo kodem odpowiedzi HTTP, switch jest czytelniejszy niż pięć kolejnych else if. Gdy warunki są zakresami albo łączą kilka zmiennych, na przykład czas > 5000 && srodowisko.equals("prod"), zostańcie przy if.

Według dokumentacji Oracle selektor klasycznej instrukcji switch może być typu byte, short, char, int, ich odpowiednikami obiektowymi, typem wyliczeniowym enum albo, od Javy SE 7 z 2011 roku, typem String. Nie zadziała z long, float, double ani boolean. Java 21 rozszerzyła tę listę o dowolne typy referencyjne, ale do tego dojdziemy w szóstej sekcji.

Forma Od wersji Javy Zwraca wartość Fall through Kiedy wybrać
Klasyczna instrukcja z dwukropkiem 1.0, String od 7 nie tak, bez break utrzymanie starego kodu
Instrukcja ze strzałką case X -> 14 (JEP 361) nie nie ma akcje zależne od wartości
Wyrażenie switch (switch expression) 14 (JEP 361) tak, także przez yield nie ma przy strzałce; z dwukropkiem i yield nadal jest przypisanie wartości do zmiennej
Dopasowanie wzorców (pattern matching) 21 (JEP 441) tak nie ma przy strzałce różne typy obiektów, case null

Źródło: OpenJDK, JEP 361 (2020) i JEP 441 (2023); Oracle, The Java Tutorials, The switch Statement.

Jak wygląda klasyczna instrukcja switch z break?

Klasyczna instrukcja switch ma etykiety zakończone dwukropkiem i instrukcję break, która kończy gałąź. Poniższy przykład zwraca godziny otwarcia sklepu dla dnia tygodnia. Kilka etykiet nad jednym blokiem kodu to legalny sposób na wspólną obsługę poniedziałku i wtorku.

GodzinyOtwarcia.java, Java 25

public class GodzinyOtwarcia {
    static String godziny(String dzien) {
        String wynik;
        switch (dzien) {
            case "poniedziałek":
            case "wtorek":
                wynik = "8:00 do 16:00";
                break;
            case "środa":
            case "czwartek":
                wynik = "8:00 do 15:00";
                break;
            case "piątek":
                wynik = "8:00 do 14:00";
                break;
            case "sobota":
                wynik = "8:00 do 12:00";
                break;
            case "niedziela":
                wynik = "Sklep nieczynny";
                break;
            default:
                wynik = "Niepoprawny dzień tygodnia: " + dzien;
        }
        return wynik;
    }

    public static void main(String[] args) {
        System.out.println(godziny("wtorek"));    // 8:00 do 16:00
        System.out.println(godziny("niedziela")); // Sklep nieczynny
        System.out.println(godziny("maj"));       // Niepoprawny dzień tygodnia: maj
    }
}

Zwróćcie uwagę na ostatnie wywołanie z wartością "maj". To miesiąc, nie dzień tygodnia, więc żadna etykieta nie pasuje i metoda zwraca komunikat z gałęzi default. Dla testera to gotowy przypadek testowy z klasy równoważności danych niepoprawnych: każda metoda ze switch powinna mieć test, który celowo wpada w default. Na naszych szkoleniach z Javy zawsze prosimy o ten test, bo najłatwiej o nim zapomnieć.

Trzy reguły, które sprawdza kompilator: wartości w etykietach muszą być stałymi znanymi w czasie kompilacji, nie mogą się powtarzać, a ich typ musi pasować do selektora. Zmienna w case skończy się błędem kompilacji, więc tej klasy pomyłek w ogóle nie zobaczycie w raporcie z testów.

Na czym polega fall through i dlaczego brak break to błąd?

Fall through, czyli przepadanie, oznacza, że po wejściu do pasującej gałęzi Java wykonuje kolejne instrukcje w dół, aż natrafi na break albo koniec bloku. Kolejne etykiety case nie są barierą. Dokumentacja Oracle opisuje to wprost: bez break instrukcje w bloku switch przepadają do następnych gałęzi, niezależnie od ich etykiet.

PriorytetBledu.java, pułapka

public class PriorytetBledu {
    static int priorytet(String waznosc) {
        int p;
        switch (waznosc) {
            case "krytyczny":
                p = 1;
            case "wysoki":
                p = 2;
                break;
            case "niski":
                p = 3;
                break;
            default:
                p = 4;
        }
        return p;
    }

    public static void main(String[] args) {
        System.out.println(priorytet("krytyczny")); // 2, a miało być 1
        System.out.println(priorytet("wysoki"));    // 2
    }
}

Błąd krytyczny dostaje tu priorytet 2, bo po przypisaniu p = 1 brakuje break i program przepada do gałęzi "wysoki". Kod się kompiluje, testy jednostkowe dla wartości "wysoki" i "niski" przechodzą, a raport defektów po cichu obniża wagę najgroźniejszych zgłoszeń. Kompilator nie zgłosi błędu, bo z jego punktu widzenia to tylko bardzo hojny priorytet.

Jak się przed tym bronić. Po pierwsze, kompilujcie z flagą -Xlint:fallthrough: javac z JDK 25 wypisze ostrzeżenie possible fall-through into case dokładnie dla linii z etykietą "wysoki", co sprawdziliśmy na tym przykładzie. Po drugie, piszcie test dla każdej gałęzi, nie tylko dla tych, które wydają się ważne. Po trzecie, a to najlepsza rada, używajcie składni ze strzałką, w której fall through po prostu nie istnieje.

Pułapka 1

Brak break

Gałąź wykonuje się razem z następną. Objaw: zła wartość tylko dla jednej etykiety, reszta testów zielona.

Pułapka 2

null w selektorze

Switch na zmiennej String, która ma wartość null, rzuca NullPointerException, zanim sprawdzi jakąkolwiek etykietę.

Pułapka 3

Brak default

Nowa wartość w danych, na przykład nowa przeglądarka w konfiguracji CI, przechodzi bez śladu i test działa na złych ustawieniach.

Pułapka 4

Wielkość liter

Etykieta “chrome” nie pasuje do “Chrome”. Porównanie Stringów w switch działa jak equals, więc normalizujcie wartość przez toLowerCase.

Jak działa switch ze strzałką z Javy 14?

Switch ze strzałką to forma, w której etykieta ma postać case WARTOŚĆ ->, a po strzałce stoi jedno wyrażenie, blok w nawiasach klamrowych albo instrukcja throw. Wykonuje się wyłącznie gałąź po strzałce, więc break jest zbędny, a fall through niemożliwy. Kilka wartości łączycie przecinkiem w jednej etykiecie.

Według JEP 361 składnia ze strzałką i switch expression weszły do standardu w Javie 14, wydanej 17 marca 2020 roku, po dwóch wersjach podglądowych w Javie 12 i 13. To oznacza, że w każdym projekcie na Javie 17 albo 21 możecie z niej korzystać bez żadnych flag kompilatora.

HarmonogramTestow.java, Java 25

public class HarmonogramTestow {
    static void zaplanuj(String dzien) {
        switch (dzien) {
            case "sobota", "niedziela" -> System.out.println("Weekend: tylko testy dymne");
            case "poniedziałek", "wtorek", "środa", "czwartek", "piątek" ->
                    System.out.println("Dzień roboczy: pełna regresja o 2:00");
            default -> throw new IllegalArgumentException("To nie jest dzień tygodnia: " + dzien);
        }
    }

    public static void main(String[] args) {
        zaplanuj("sobota"); // Weekend: tylko testy dymne
        zaplanuj("środa");  // Dzień roboczy: pełna regresja o 2:00
        zaplanuj("maj");    // IllegalArgumentException: To nie jest dzień tygodnia: maj
    }
}

Ten sam dzień tygodnia, który w klasycznej wersji trafiał po cichu do default, tutaj kończy się wyjątkiem z czytelnym komunikatem. W kodzie testów to zwykle lepszy wybór: test na złej konfiguracji powinien przerwać przebieg od razu, a nie wykonać się na domyślnych ustawieniach i dać mylący wynik. Gałąź case "sobota", "niedziela" to dokładnie ta konstrukcja, której brakowało w pierwszej wersji tej lekcji.

Czym jest switch expression i do czego służy yield?

Switch expression to switch użyty jako wyrażenie: cały blok zwraca wartość, którą przypisujecie do zmiennej albo zwracacie z metody, a całość kończy się średnikiem po nawiasie klamrowym. Kompilator wymaga, żeby wyrażenie było wyczerpujące, czyli obsłużyło każdą możliwą wartość selektora. Dla typu enum wystarczy wymienić wszystkie stałe i default nie jest potrzebny.

Konfiguracja.java, Java 25

public class Konfiguracja {
    enum Srodowisko { DEV, TEST, STAGING, PROD }

    static String bazowyUrl(Srodowisko s) {
        return switch (s) {
            case DEV -> "http://localhost:8080";
            case TEST -> "https://test.sklep.example";
            case STAGING -> "https://staging.sklep.example";
            case PROD -> "https://sklep.example";
        };
    }

    static int liczbaPowtorzen(Srodowisko s) {
        return switch (s) {
            case DEV, TEST -> 0;
            case STAGING -> 1;
            case PROD -> {
                System.out.println("Produkcja: uruchamiamy tylko testy dymne");
                yield 2;
            }
        };
    }

    public static void main(String[] args) {
        Srodowisko s = Srodowisko.valueOf(System.getProperty("env", "TEST"));
        System.out.println(bazowyUrl(s));       // https://test.sklep.example
        System.out.println(liczbaPowtorzen(s)); // 0
    }
}

Gdy gałąź potrzebuje kilku instrukcji, piszecie blok w nawiasach klamrowych i zwracacie wartość słowem yield, jak w gałęzi PROD. Według JEP 361 słowo yield powstało właśnie po opiniach z Javy 12, w której wartość zwracano przez przeciążony break i programiści uznali to za mylące. Dziś break znowu znaczy tylko jedno: wyjdź z instrukcji.

Wyczerpujące wyrażenie daje testerowi coś cennego: gdy ktoś dopisze do enuma Srodowisko piątą stałą, na przykład UAT, metoda bazowyUrl przestanie się kompilować, dopóki nie dostanie nowej gałęzi. Błąd wychodzi na etapie budowania w CI, a nie w nocnym przebiegu regresji na nieistniejącym adresie. Z naszego doświadczenia to jedna z najtańszych poprawek stabilności frameworka, jakie możecie zrobić w jedno popołudnie.

Jak switch zmieniał się w kolejnych wersjach Javy

2011

Java SE 7: String jako selektor instrukcji switch, według Oracle

2020

Java 14: składnia ze strzałką i switch expression, JEP 361

2023

Java 21: dopasowanie wzorców i case null, JEP 441

35%

aplikacji monitorowanych przez New Relic działało na Javie 17 w 2024 roku

Źródło: Oracle, The Java Tutorials; OpenJDK, JEP 361 i JEP 441; New Relic, 2024 State of the Java Ecosystem.

Co daje dopasowanie wzorców w switch z Javy 21?

Dopasowanie wzorców w switch, finalnie wprowadzone w Javie 21 przez JEP 441, pozwala sprawdzać nie tylko wartość, ale też typ obiektu i rozkładać rekordy na składowe. Według OpenJDK funkcja przeszła cztery wersje podglądowe w Javach od 17 do 20, zanim weszła do standardu 19 września 2023 roku. Do tego dochodzą strażniki when i etykieta case null.

Najlepiej widać to na wynikach testów. Zapieczętowany interfejs sealed mówi kompilatorowi, że wynik może być tylko zaliczony, niezaliczony albo pominięty, więc switch po tych trzech typach jest wyczerpujący bez default.

RaportTestow.java, Java 25

public class RaportTestow {
    sealed interface WynikTestu permits Zaliczony, Niezaliczony, Pominiety {}
    record Zaliczony(String nazwa, long czasMs) implements WynikTestu {}
    record Niezaliczony(String nazwa, String powod) implements WynikTestu {}
    record Pominiety(String nazwa) implements WynikTestu {}

    static String opis(WynikTestu w) {
        return switch (w) {
            case null -> "Brak wyniku: sprawdźcie konfigurację raportu";
            case Zaliczony z when z.czasMs() > 5000 -> "WOLNY: " + z.nazwa() + " (" + z.czasMs() + " ms)";
            case Zaliczony z -> "OK: " + z.nazwa();
            case Niezaliczony(String nazwa, String powod) -> "BŁĄD: " + nazwa + ", " + powod;
            case Pominiety p -> "POMINIĘTY: " + p.nazwa();
        };
    }

    public static void main(String[] args) {
        System.out.println(opis(new Zaliczony("logowanie", 1200)));         // OK: logowanie
        System.out.println(opis(new Zaliczony("koszyk", 7300)));            // WOLNY: koszyk (7300 ms)
        System.out.println(opis(new Niezaliczony("płatność", "timeout")));  // BŁĄD: płatność, timeout
        System.out.println(opis(null));                                     // Brak wyniku: ...
    }
}

W tym przykładzie dzieją się cztery rzeczy naraz. case Zaliczony z when z.czasMs() > 5000 wyłapuje wolne testy przed zwykłymi zaliczonymi, bo etykiety sprawdzane są od góry. case Niezaliczony(String nazwa, String powod) rozkłada rekord na pola bez wywoływania getterów. case null obsługuje brak wyniku zamiast rzucać wyjątek. A gdy ktoś doda czwarty typ wyniku, na przykład Przerwany, kompilator wskaże każdy switch do uzupełnienia.

Dlaczego null wywraca switch

Uruchomcie instrukcję switch na zmiennej String z wartością null, na przykład z System.getProperty("browser"), gdy zapomnicie podać parametru. Java 21 rzuca wtedy NullPointerException z komunikatem Cannot invoke "String.hashCode()". Nikt w kodzie nie wywołuje hashCode, ale javac kompiluje switch na tekstach do porównania skrótów hashCode, a dopiero potem do equals. Komunikat zdradza więc, jak działa maszyneria pod spodem. Według JEP 441 jednym z celów Javy 21 było właśnie złagodzenie tej historycznej wrogości switch wobec null: wystarczy dopisać case null.

Jeśli Wasz projekt stoi na Javie 17, macie już strzałkę i switch expression, ale nie macie finalnych wzorców. Według raportu New Relic z 2024 roku w ciągu sześciu miesięcy od premiery Javy 21 korzystało z niej 1,4 procent monitorowanych aplikacji, czyli migracja trwa. Przy nowym frameworku testów warto od razu wybrać Javę 25, najnowszą wersję z długim wsparciem, na której prowadzimy cały kurs.

Jak testerzy używają switch w automatyzacji testów?

W testach automatycznych switch pojawia się najczęściej w trzech miejscach: przy wyborze przeglądarki, przy wyborze środowiska i przy mapowaniu danych testowych na oczekiwane wyniki. Pierwszy przypadek to klasyczna fabryka sterowników w Selenium 4, w której nazwę przeglądarki podajecie parametrem z linii poleceń, na przykład mvn test -Dbrowser=firefox.

FabrykaPrzegladarek.java, Selenium 4.51.0 i Java 25

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

public class FabrykaPrzegladarek {
    public static WebDriver utworz() {
        String nazwa = System.getProperty("browser", "chrome");
        return switch (nazwa.toLowerCase()) {
            case "chrome" -> new ChromeDriver();
            case "firefox" -> new FirefoxDriver();
            case "edge" -> new EdgeDriver();
            default -> throw new IllegalArgumentException("Nieobsługiwana przeglądarka: " + nazwa);
        };
    }
}

Selenium Manager wbudowany w Selenium 4 sam pobiera pasujący sterownik, więc fabryka nie potrzebuje ścieżek do plików chromedriver. Ten sam wzorzec, rozbudowany o opcje przeglądarek i uruchamianie zdalne, omawiamy w tekście o wzorcach projektowych w Selenium WebDriver, a o tym, jak rozplanować przebiegi na kilku przeglądarkach, piszemy w artykule o testach na różnych przeglądarkach w Sauce Labs i BrowserStack.

W przeglądach frameworków testowych u naszych klientów regularnie wracają dwie poprawki związane ze switch: brak gałęzi default rzucającej wyjątek, przez który literówka w nazwie przeglądarki uruchamia cały pakiet w Chrome, oraz rozrośnięte instrukcje z kilkudziesięcioma etykietami, które powinny być mapą albo osobnymi klasami. Według danych Quality Island zespół napisał już ponad 450 000 testów automatycznych i ten wzorzec wraca w niemal każdym frameworku. Jeśli chcecie przećwiczyć go na prawdziwym projekcie, zajmujemy się tym na szkoleniu automatyzacja testów z Selenium WebDriver, a gdy Wasz zespół potrzebuje wsparcia przy budowie frameworka, pomagamy w ramach usługi automatyzacji testów.

Dlaczego to wciąż Selenium, a nie wyłącznie nowsze narzędzia, analizuje Strefa QA w tekście o Selenium w 2026 roku. Dla nauki Javy wybór narzędzia ma mniejsze znaczenie: switch działa tak samo w testach Selenium, Playwright dla Javy i REST Assured.

Kiedy lepiej nie używać instrukcji switch?

Instrukcja switch przestaje się opłacać, gdy zamiast wybierać zachowanie zaczyna przechowywać dane. Jeśli każda gałąź tylko zwraca tekst albo liczbę, a etykiet jest kilkanaście, czytelniejsza będzie mapa Map.of("chrome", 2, "firefox", 3), którą możecie wczytać z pliku konfiguracyjnego bez ponownej kompilacji. Jeśli każda gałąź wykonuje inny, rozbudowany algorytm, lepszy jest polimorfizm: osobna klasa dla każdego wariantu, o którym więcej w lekcji o programowaniu obiektowym w Javie.

Sytuacja Narzędzie Dlaczego
Jedna zmienna, kilka stałych wartości switch ze strzałką czytelny, bez fall through
Zakresy i warunki złożone, np. czas > 5000 if i else if switch porównuje tylko równość i wzorce
Wartość zależna od stałej enum switch expression bez default kompilator wymusi obsługę nowych stałych
Kilkanaście par klucz i wartość Map albo plik konfiguracyjny zmiana danych bez kompilacji
Różne obiekty o wspólnym interfejsie polimorfizm albo switch z wzorcami logika przy typie, nie w jednym bloku

Źródło: opracowanie Quality Island na podstawie JLS Java SE 25, rozdział 14.11 i 15.28.

Kolejny krok w kursie to lekcja 12, łańcuchy znakowe, które przydadzą się przy każdym switch na tekstach. Jeśli pominęliście lekcję 10, wróćcie jeszcze do pętli w Javie. Jeśli wolicie przejść całą ścieżkę z trenerem, od zmiennych po pierwszy test automatyczny, zapraszamy na szkolenie programowanie Java dla testerów albo na kurs wprowadzenie do programowania od podstaw w Javie. W Quality Island przeszkoliliśmy już ponad 2 500 specjalistów i wiemy, że switch to temat na jedno popołudnie, a nawyk pisania testu dla gałęzi default zostaje na lata.

Wartości, które sprawdzacie w switch, często pochodzą z tablicy, na przykład listy środowisk albo przeglądarek. Jak taką tablicę zadeklarować i czym różni się od ArrayList, pokazujemy w lekcji tablice w Javie.

Co zabrać z tego artykułu

01Instrukcja switch wybiera gałąź kodu na podstawie wartości jednego selektora. Do warunków złożonych i zakresów zostańcie przy if.

02W klasycznej składni brak break powoduje fall through. Kompilujcie z -Xlint:fallthrough i piszcie test dla każdej gałęzi, także default.

03Od Javy 14 używajcie składni ze strzałką: case "sobota", "niedziela" -> nie przepada do kolejnych gałęzi.

04Switch expression zwraca wartość, a przy enum bez default kompilator wymusi obsługę każdej nowej stałej.

05Java 21 dodaje wzorce typów, rekordy, strażniki when i case null. Bez case null switch na null rzuca NullPointerException.

Źródło: OpenJDK JEP 361 i 441; JLS Java SE 25; kod skompilowany javac --release 25.

Chcecie pisać testy automatyczne w Javie bez zgadywania składni? Na szkoleniu Java dla testerów przerabiamy switch, pętle, kolekcje i pierwszy framework na prawdziwych przykładach.

Zobacz szkolenie Java

Poprzednia lekcja10. Pętle w JavieSpis kursuWszystkie lekcje kursu JavyNastępna lekcja12. Łańcuchy znakowe

Powiązane na blogu Quality Island

Pełna lista źródeł

Co o tym sądzisz?

Dodaj komentarz

Bądź na bieżąco
Strefa QA, portal z eksperckimi artykułami o jakości oprogramowania
Bądź na bieżąco
Szkolenie Specjalista ds. AI w organizacji: program przekwalifikowujący
Specjalista ds. AI w organizacji
Specjalista ds. AI w organizacji: program przekwalifikowujący

4 400,00 zł

03.02.27, 14.04.27, 02.06.27
5 dni
Szkolenie Inżynier AI: program przekwalifikowujący dla specjalistów IT
Inżynier AI
Inżynier AI: program przekwalifikowujący dla specjalistów IT

6 900,00 zł

12.01.27, 16.03.27
8 tygodni
KURS ONLINE: PAQT Foundation, AI w pracy testera, przygotowanie do egzaminu

590,00 zł

Popularne artykuły
Język Gherkin: co to jest i jak go używać w testowaniu oprogramowania, grafika Quality Island
Język Gherkin: co to jest i jak go używać w testowaniu oprogramowania
Smoke test: co to jest i czym różni się od sanity testu, grafika Quality Island
Smoke test: co to jest, kiedy go uruchamiać i czym różni się od sanity testu
Jak zostać testerem oprogramowania: ścieżka krok po kroku, grafika Quality Island
Jak zostać testerem oprogramowania: ścieżka krok po kroku bez dyplomu informatyka
Najnowsze artykuły
Audyt WCAG 2.2: przebieg, ceny w zł i szablon zapytania ofertowego, okładka artykułu Quality Island
Audyt WCAG 2.2: przebieg, ceny w zł i szablon zapytania ofertowego
Tester manualny: co robi, ile zarabia i checklista kompetencji na pierwszą rozmowę, grafika Quality Island
Tester manualny: co robi, ile zarabia w 2026 i checklista kompetencji na rozmowę
Art. 4 AI Act: kompetencje w zakresie AI w zespole IT i QA, checklista i rejestr szkoleń AI
Art. 4 AI Act po Digital Omnibus: kompetencje w zakresie AI w zespole IT i QA, checklista i rejestr szkoleń
Popularne kategorie

Grupa marek jakości oprogramowania

Jakość oprogramowania w Polsce ma swój adres

Quality Island