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.
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:
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.
Zadanie 6: System zarządzania uczelnianymi
kursami
Stwórz program symulujący
system zarządzania kursami na uczelni.
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.