Obraz zawierający tekst, Czcionka, Grafika

Opis wygenerowany automatycznie 

Kierunek Informatyka

 

Instrukcja do ćwiczeń laboratoryjnych nr:

6

Nazwa przedmiotu:
Programowanie w języku Java

Temat:  Klasy abstrakcyjne

Tryb studiów: stacjonarne

Czas trwanie ćw.

2x45 min

Autor materiałów: dr Marcin Skuba

 

1. Treści programowe:

Programowanie obiektowe,  klasy abstrakcyjne i anonimowe – deklaracja i wykorzystanie

 

2. Cel zajęć:

Zrozumienie zasad tworzenia i wykorzystania klas abstrakcyjnych oraz ich wykorzystania poprzez dziedziczenie lub przez klasy anonimowe   

3. Materiały dydaktyczne:

 

·       Klasy abstrakcyjne

 

Aby zadeklarować klasę abstrakcyjną, użyj słowa kluczowego abstract przed słowem class. Możesz również zadeklarować w niej metody abstrakcyjne, które nie posiadają ciała, oraz zwykłe metody z pełną implementacją.

 

Klasy abstrakcyjne są idealne, gdy chcesz zdefiniować wspólne cechy i zachowania dla grupy powiązanych klas, ale szczegóły implementacji pozostawić podklasom. Stosuje się je, gdy:

 

Cechy klas abstrakcyjnych:

·       mogą zawierać metody abstrakcyjne (metody nie posiadające implementacji),

·       mogą zawierać zwykłe metody,

·       mogą zawierać stałe statyczne,

·       klasa abstrakcyjna może być rozszerzana przez inne klasy Java, zarówno zwykłe jak i abstrakcyjne,

·       klasa dziedzicząca klasę abstrakcyjną musi stworzyć implementację dla metod abstrakcyjnych,

·       metody abstrakcyjne nie mogą być statyczne, ponieważ nie posiadają implementacji,

·       w klasach abstrakcyjnych możemy tworzyć konstruktory,

·       tworząc instancję klasy abstrakcyjnej musimy stworzyć implementację dla metod abstrakcyjnych.

 

Ideą metody abstrakcyjnej jest to, że jest ona wymogiem dla każdej klasy, która dziedziczy po klasie abstrakcyjnej. To klasa dziedzicząca jest zobowiązana dostarczyć konkretną implementację. Dzięki temu można wymusić pewne zachowania na podklasach, ale pozostawić im elastyczność w ich realizacji.

 

 

- Przykład tworzenia i wykorzystania klas abstrakcyjnych poprzez typowe dziedziczenie

 

Klasa Figura to nasz szablon. Wymaga ona, by każda klasa dziedzicząca miała metodę obliczPole(), ale nie narzuca, jak to pole ma być liczone. Dzięki temu mamy pewność, że wszystkie figury będą miały tę funkcjonalność.

 

Dodanie konstruktora do klasy abstrakcyjnej pozwala na inicjalizowanie wspólnych pól we wszystkich klasach dziedziczących. Zamiast powtarzać kod (this.typ = "...") w każdym konstruktorze podklasy, możemy go umieścić w jednym miejscu. Dzięki temu kod jest bardziej czytelny i łatwiejszy w utrzymaniu. Obowiązkowe wywołanie super() zapewnia, że każde Kolo i Kwadrat poprawnie zdefiniuje swój typ już w momencie tworzenia.

 

// Klasa abstrakcyjna Figura
abstract class Figura {
   
protected String typ;

   
// Konstruktor klasy abstrakcyjnej
   
public Figura(String typ) {
       
this.typ = typ;
    }

   
public abstract double obliczPole(); // Abstrakcyjna metoda - musi być nadpisana

   
public void pokazTypFigury() {
        System.out.println(
"Jestem figurą typu: " + typ);
    }
}

 

 

Klasy Kolo i Kwadrat dostarczają konkretne implementacje tej metody, dostosowane do swoich potrzeb. Użycie adnotacji @Override podkreśla, że nadpisujemy metodę z klasy nadrzędnej.

 

 

// Klasa Koło dziedziczy po Figura
class Kolo extends Figura {
   
private double promien;

   
public Kolo(double promien) {


       
// Wywołanie konstruktora klasy nadrzędnej
       
super("Koło");
       
this.promien = promien;
    }

   
@Override
   
public double obliczPole() {
       
return Math.PI * promien * promien;
    }
}

 

// Klasa Kwadrat dziedziczy po Figura
class Kwadrat extends Figura {
   
private double bok;

   
public Kwadrat(double bok) {


       
// Wywołanie konstruktora klasy nadrzędnej
       
super("Kwadrat");
       
this.bok = bok;
    }

   
@Override
   
public double obliczPole() {
       
return bok * bok;
    }
}

 

W metodzie main, tworzymy obiekty Kolo i Kwadrat, ale przypisujemy je do zmiennych typu Figura. Jest to możliwe, ponieważ Kolo i Kwadrat są typem Figura.

Kiedy wywołujemy kolo.obliczPole(), Java wie, że ma użyć metody z klasy Kolo, a kiedy wywołujemy kwadrat.obliczPole(), używa metody z klasy Kwadrat.

To zachowanie nazywamy polimorfizmem.

 

public class KalkulatorFigur {
   
public static void main(String[] args) {


       
// Tworzenie obiektów podklas
       
Figura kolo = new Kolo(5.0);
        Figura kwadrat =
new Kwadrat(4.0);

       
// Wywołanie metody z ciałem, która została odziedziczona
       
kolo.pokazTypFigury();
        System.out.println(
"Pole: " + kolo.obliczPole());

        System.out.println(
"------------------------------------");

       
// Wywołanie metody z ciałem i metody abstrakcyjnej
       
kwadrat.pokazTypFigury();
        System.out.println(
"Pole: " + kwadrat.obliczPole());
    }
}

 

 

- Przykład tworzenia i wykorzystania klas abstrakcyjnych poprzez stworzenie klas anonimowych

 

W przykładzie poniżej, klasy anonimowe Kolo i Kwadrat są jednorazowe. Zamiast tworzyć oddzielne pliki (Kolo.java, Kwadrat.java), definiujemy ich zachowanie w miejscu, w którym tworzymy obiekt. Dzięki temu oszczędzamy kod i uzyskujemy elastyczność.

 

Klasy anonimowe są szczególnie przydatne, gdy potrzebujesz prostej implementacji dla interfejsu lub abstrakcyjnej klasy, która będzie użyta tylko w jednym miejscu.

 

public class KalkulatorFigur {
   
public static void main(String[] args) {
       
// Tworzenie obiektu Koło jako klasy anonimowej.
        // Wywołanie konstruktora klasy nadrzędnej przez super("Koło").
       
Figura kolo = new Figura("Koło") {
           
private double promien = 5.0;

           
@Override
           
public double obliczPole() {
               
return Math.PI * promien * promien;
            }
        };

       
// Tworzenie obiektu Kwadrat jako klasy anonimowej.
        // Wywołanie konstruktora klasy nadrzędnej przez super("Kwadrat").
       
Figura kwadrat = new Figura("Kwadrat") {
           
private double bok = 4.0;

           
@Override
           
public double obliczPole() {
               
return bok * bok;
            }
        };

       
// Wyświetlanie danych dla Koła.
       
kolo.pokazTypFigury();
        System.out.println(
"Pole: " + kolo.obliczPole());
        System.out.println(
"------------------------------------");

       
// Wyświetlanie danych dla Kwadratu.
       
kwadrat.pokazTypFigury();
        System.out.println(
"Pole: " + kwadrat.obliczPole());
    }

}

 

4. Zadania

 


Zadanie  1.  System zarządzania kontami bankowymi

Zadanie: Stwórz abstrakcyjną klasę KontoBankowe z polami numerKonta i saldo. Dodaj do niej abstrakcyjną metodę wyplata(double kwota) i zwykłą metodę wplata(double kwota). Następnie zaimplementuj dwie klasy podrzędne: KontoOsobiste i KontoFirmowe. W klasie KontoOsobiste metoda wyplata powinna odejmować kwotę, a w klasie KontoFirmowe powinna dodawać opłatę transakcyjną, np. 0.5% od wypłacanej kwoty. Wykorzystaj mechanizm dziedziczenia.

 


Zadanie 2. Hierarchia oprogramowania

Zaprojektuj abstrakcyjną klasę Oprogramowanie z metodą uruchom() i polem wersja. Dodaj abstrakcyjną metodę pobierzWersje(). Następnie utwórz klasy SystemOperacyjny i Aplikacja, które dziedziczą po Oprogramowanie. W SystemOperacyjny zaimplementuj pobierzWersje tak, aby zwracała wersję w formacie 10.1, a w Aplikacja – w formacie 2.0 beta. Wykorzystaj mechanizm dziedziczenia. Klasy SystemOperacyjny i Aplikacja stwórz jako klasy anonimowe.  

 


Zadanie 3. Wypożyczalnia sprzętu sportowego

Stwórz klasę abstrakcyjną SprzetSportowy z polami nazwa i cena oraz abstrakcyjną metodą obliczKosztWynajmu(int dni). Następnie stwórz dwie klasy: Narty i Rower. Klasa Narty powinna liczyć koszt wynajmu, dodając stałą opłatę za przygotowanie sprzętu, np. 20 zł, a klasa Rower – opłatę za kask w wysokości 5 zł za dzień. Wykorzystaj mechanizm dziedziczenia.

 


Zadanie 4. Kreator postaci do gry RPG

Utwórz abstrakcyjną klasę Postac z polami sila, zrecznosc i abstrakcyjną metodą atakuj(). Następnie stwórz dwie klasy dziedziczące: Rycerz i Lucznik. Klasa Rycerz powinna implementować metodę atakuj, zadając obrażenia równe sila * 1.5, a Lucznikzrecznosc * 2.0. W main stwórz tablicę postaci i dla każdej z nich wywołaj metodę atakuj(). Wykorzystaj mechanizm dziedziczenia.

 


Zadanie 5. System powiadomień

Stwórz abstrakcyjną klasę Powiadomienie z abstrakcyjną metodą wyslijPowiadomienie(String wiadomosc). Następnie utwórz dwie klasy podrzędne: PowiadomienieEmail i PowiadomienieSMS. W klasie PowiadomienieEmail metoda wyslijPowiadomienie powinna dodawać do wiadomości temat wiadomości, a w klasie PowiadomienieSMS – limit znaków, jeśli wiadomość jest za długa, np. 160 znaków. Klasy PowiadomienieEmail i PowiadomienieSMS utwórz jako klasy anonimowe.

 


Zadanie 6. Własny pomysł 

Napis program według własnego pomysłu, w którym pokaż wykorzystanie klasy abstrakcyjnej.