Obraz zawierający tekst, Czcionka, Grafika

Opis wygenerowany automatycznie 

Kierunek Informatyka

 

Instrukcja do ćwiczeń laboratoryjnych nr:

5

Nazwa przedmiotu:
Programowanie w języku Java

Temat:  Klasy anonimowe i klasy wewnętrzne

Tryb studiów: stacjonarne

Czas trwanie ćw.

2x45 min

Autor materiałów: dr Marcin Skuba

 

1. Treści programowe:

Programowanie obiektowe,  klasy anonimowe – deklaracja, klasy wewnętrzne.

 

2. Cel zajęć:

Zrozumienie zasad tworzenia i wykorzystania klas anonimowych oraz klas wewnętrznych: non-static inner classes, static nested classes oraz local inner classes.  

 

3. Materiały dydaktyczne:

 

·      Klasy anonimowe

Klasy anonimowe w Javie to sposób na tworzenie obiektów, które są jednorazowego użytku. Są to klasy bez nazwy, definiowane i instancjonowane jednocześnie. Nie możesz ich używać ponownie w kodzie, ale są idealne, gdy potrzebujesz jednorazowej implementacji interfejsu lub rozszerzenia klasy.

 

Zamiast pisać pełną definicję klasy, robisz to "w locie" bezpośrednio w miejscu, gdzie tworzysz obiekt. Dzięki temu oszczędzasz czas i kod, bo nie musisz deklarować oddzielnej klasy tylko po to, żeby raz jej użyć.

 

// Zwykła klasa z metodą, którą możemy nadpisać
class Pracownik {
   
public void wykonajPrace() {
        System.
out.println("Pracownik wykonuje ogólne zadanie.");
    }
}

 

public class Firma {
    public static void main(String[] args) {
        // Tworzymy anonimowy obiekt, który dziedziczy po klasie Pracownik
       
Pracownik programista = new Pracownik() {
            @Override
           
public void wykonajPrace() {
                // Nadpisujemy metodę, aby zmienić jej zachowanie
               
System.out.println("Programista pisze kod w Javie.");
            }
        };

        // Wywołujemy nadpisaną metodę na obiekcie klasy anonimowej
       
programista.wykonajPrace();

        // Możemy też stworzyć zwykły obiekt Pracownika dla porównania
       
Pracownik zwyklyPracownik = new Pracownik();
        zwyklyPracownik.wykonajPrace();
    }
}

 

Główna idea polega na tym, że klasa anonimowa implementuje interfejs lub dziedziczy po innej klasie.

 

W tym przykładzie, new Pracownik() { ... } tworzy jednorazową klasę, która dziedziczy po Pracownik. Wewnątrz tej klasy anonimowej, nadpisujemy metodę wykonajPrace(), zmieniając jej zachowanie. Kiedy wywołujemy programista.wykonajPrace(), uruchamia się kod z klasy anonimowej, a nie ten z klasy Pracownik.

 

 

·       Klasy wewnętrze

Klasy wewnętrzne to klasy zdefiniowane wewnątrz innej klasy. Pozwalają na logiczne grupowanie klas, które są używane tylko w jednym miejscu. Klasy te mają dostęp do wszystkich składowych klasy zewnętrznej, nawet tych prywatnych.

 

Wyróżniamy kilka rodzajów klas wewnętrznych, w zależności od ich kontekstu i modyfikatorów:

 

- Zwykłe klasy wewnętrzne (non-static inner classes)

 

Są najczęściej spotykane. Każda instancja takiej klasy jest powiązana z instancją klasy zewnętrznej.

Niestatyczne klasy wewnętrzne, znane też jako zwykłe klasy wewnętrzne, to klasy zadeklarowane wewnątrz innej klasy, ale bez słowa kluczowego static.

  1. Silne Powiązanie z Instancją Zewnętrzną:

 

 

public class Samochod {
   
private String marka;
   
private String kolor;

   
//Konstruktor klasy zewnętrznej
   
public Samochod(String marka, String kolor) {
       
this.marka = marka;
       
this.kolor = kolor;
    }

   
//Metoda tworząca u uruchamiająca silnik
   
public void uruchom(){
        Silnik silnik =
new Silnik();
        System.
out.println("Samochod marki "+marka+" uruchamia silnik.");
        silnik.start();
    }

   
//Klasa wewnętrzna - Silnik. Ma dostęp do prywatnych pół Samochod
   
class Silnik{
       
private String typ ="benzynowy";
       
public void start() {
            System.
out.println("Silnik " + typ + " o kolorze " + kolor + " uruchomiony!!!");
        }
    }
}

 

 

//Klasa główna programu
public class UruchomienieSamochodu {
   
public static void main(String[] args) {
        Samochod samochod =
new Samochod("Ford", "niebieski");
        samochod.uruchom();
    }
}

 

- Statyczne klasy wewnętrzne (static nested classes)

Działają podobnie do zwykłych klas, ale nie są powiązane z konkretnym obiektem klasy zewnętrznej. Dostęp do ich składowych jest taki sam, jak do statycznych składowych klasy zewnętrznej. Nie mogą one jednak bezpośrednio odwoływać się do niestatycznych składowych klasy zewnętrznej.

Statyczna klasa wewnętrzna jest kompilowana jako osobna klasa i zachowuje się jak zwykła klasa najwyższego poziomu, z tą różnicą, że jej pełna nazwa zawiera nazwę klasy zewnętrznej (np. Samochod.Silnik).

·       Statyczna klasa wewnętrzna ma dostęp tylko do statycznych składowych (pól i metod) klasy zewnętrznej. Nie ma dostępu do jej niestatycznych (instancyjnych) pól.

·       Nie wymaga wcześniejszego utworzenia instancji klasy zewnętrznej. Tworzymy obiekt bezpośrednio, używając nazwy klasy zewnętrznej.

 

W przeciwieństwie do niestatycznych klas wewnętrznych (ang. non-static inner classes), statyczne klasy wewnętrzne nie przechowują ukrytego odniesienia do obiektu klasy zewnętrznej. Oszczędza to pamięć i zapobiega potencjalnym wyciekom pamięci.

 

public class Samochod {
   
private String marka;
   
private static int liczbaKol = 4; // Statyczna zmienna klasy zewnętrznej

   
public Samochod(String marka) {
       
this.marka = marka;
    }

   
// Statyczna klasa wewnętrzna
    // Ma dostęp tylko do statycznych składowych klasy zewnętrznej
   
public static class Silnik {
       
private String typ = "elektryczny";

       
public void pokazDaneSilnika() {
           
// Klasa Silnik ma dostęp do statycznej zmiennej
            // klasy zewnętrznej (liczbaKol)
           
System.out.println("Typ silnika: " + this.typ);
            System.
out.println("Liczba kół: " + liczbaKol);

           
// Poniższa linia spowoduje błąd kompilacji,
            // ponieważ statyczna klasa nie ma dostępu do
            // niestatycznych pól (marka)
            // System.out.println("Marka: " + marka);
       
}
    }
}

 

public class Main {
   
public static void main(String[] args) {
       
// Tworzenie obiektu klasy zewnętrznej
       
Samochod auto = new Samochod("Tesla");

       
// Tworzenie obiektu statycznej klasy wewnętrznej.
        // Używamy nazwy klasy zewnętrznej.
       
Samochod.Silnik silnik = new Samochod.Silnik();

       
// Wywołanie metody z klasy wewnętrznej
       
silnik.pokazDaneSilnika();
    }
}

 

- Lokalne klasy wewnętrzne (local inner classes):

 

Klasy lokalne to typ klas wewnętrznych zdefiniowanych wewnątrz bloku kodu, najczęściej wewnątrz metody (lub konstruktora, czy bloku inicjalizacyjnego).

Stosujemy je, gdy potrzebujemy użyć klasy:

  1. Tylko w obrębie jednej metody: Są idealne, gdy klasa pomocnicza jest potrzebna do wykonania bardzo specyficznego zadania i nie ma sensu, aby była widoczna poza tą metodą.
  2. Z dostępem do lokalnych zmiennych metody: Klasa lokalna może odwoływać się do zmiennych lokalnych metody, w której została zdefiniowana, pod warunkiem, że te zmienne są efektywnie finalne (tzn. ich wartość nie zmienia się po inicjalizacji).

 

 

public class Operacje {
   
public void przetwarzaj(int liczba) {
       
// Lokalna klasa wewnętrzna zdefiniowana wewnątrz metody.
       
class PrzetwarzaczLiczb {
           
private int wartosc;

           
public PrzetwarzaczLiczb(int wartosc) {
               
this.wartosc = wartosc;
            }

           
public void pokazInformacje() {
               
// Lokalna klasa wewnętrzna ma dostęp do zmiennych
                // metody, w której została zdefiniowana (tutaj 'liczba').
               
System.out.println("Oryginalna liczba: " + liczba);
                System.
out.println("Wartość w obiekcie: " + wartosc);
            }
        }

       
// Utworzenie obiektu lokalnej klasy wewnętrznej.
       
PrzetwarzaczLiczb przetwarza = new PrzetwarzaczLiczb(liczba * 2);

       
// Wywołanie metody na obiekcie.
       
przetwarza.pokazInformacje();
    }
}

 

public class Main {
   
public static void main(String[] args) {
        Operacje operacje =
new Operacje();
        operacje.przetwarzaj(
10);
    }
}

 

4. Zadania

 


Zadanie  1. Walidator danych użytkownika

Zadanie: Stwórz klasę Walidator, która ma metodę waliduj(String dane). Metoda ta sprawdza czy są jakiekolwiek dane i wyświetla odpowiedni komunikat „Dane są poprawne” lub „brak danych”.

W metodzie main, użyj klasy anonimowej, która dziedziczy po Walidatorze i nadpisuje metodę waliduj(), aby sprawdzała, czy podany łańcuch znaków ma co najmniej 8 liter. Jeśli łańcuch ma mniej niż 8 liter, program powinien wyświetlić "Błąd: dane są za krótkie", w przeciwnym razie "Dane poprawne".

 


Zadanie  2. System powiadomień SMS

Zadanie: Stwórz klasę Wysylacz, która ma metodę wyslij(String wiadomosc). Metoda ta powinna domyślnie wyświetlać "Wiadomość wysłana tradycyjną metodą". W metodzie main, stwórz anonimową klasę, która nadpisuje metodę wyslij(), aby symulować wysyłanie wiadomości SMS z dodatkowym tekstem, np. "Wiadomość SMS o treści: '...'"

 


Zadanie   3. Symulator zwierzęcego głosu

Zadanie: Zdefiniuj klasę Zwierze z metodą wydajGlos(), która domyślnie wyświetla "To zwierzę nie wydaje głosu". W metodzie main, stwórz dwie anonimowe klasy. Pierwsza powinna dziedziczyć po Zwierze i nadpisywać wydajGlos(), aby wyświetlała "Miau", a druga, aby wyświetlała "Hau". Wywołaj metodę wydajGlos() dla obu obiektów, demonstrując różne zachowania.

 


Zadanie   4. Kalkulator napiwków

Zadanie: Stwórz klasę Kalkulator, która ma metodę obliczNapiwek(double kwota). Domyślna implementacja powinna zwracać kwotę napiwku równą 10% rachunku. W metodzie main, utwórz klasę anonimową, która nadpisuje obliczNapiwek(), aby zwracała stałą wartość, np. 5 złotych, niezależnie od kwoty rachunku. Na koniec wyświetl napiwek dla tej samej kwoty (np. 100 zł) z użyciem obu kalkulatorów.


Zadanie  5: Symulator systemu audio

Stwórz program symulujący system audio w samochodzie.

  1. Klasa Samochod:
  2. Klasa Odtwarzacz (non-static inner class):
  3. Klasa Main:

 


Zadanie 6: System zarządzania uczelnianymi kursami

Stwórz program symulujący system zarządzania kursami na uczelni.

  1. Klasa Kurs:
  2. Klasa Ocena (static nested class):
  3. Klasa Uczelnia:

 


Zadanie 7: System przetwarzania zamówień

Stwórz program, który symuluje system przetwarzania zamówień. Zadanie polega na walidacji i podsumowaniu koszyka klienta, ale z wykorzystaniem klasy lokalnej, która będzie służyć tylko wewnątrz jednej metody.

  1. Klasa Zamowienie:
  2. Klasa Walidator (local inner class):
  3. Klasa Main: